JSON 타입을 사용해야 하는 경우
JSON 타입은 구조가 동적이거나 예측하기 어려운 JSON 객체 안의 특정 필드를 쿼리하고, 필터링하고, 집계할 수 있도록 설계되었습니다. 이를 위해 JSON 객체를 별도의 서브컬럼으로 분할하며, 그 결과 읽어야 하는 데이터 양이 크게 줄어들고 Map 또는 문자열 파싱 같은 대안보다 선택한 필드에 대한 쿼리 속도가 크게 빨라집니다.
하지만 여기에는 중요한 절충점이 있습니다:
- 더 느린
INSERTs - JSON을 서브컬럼으로 분할하고, 타입을 추론하고, 유연한 저장 구조를 관리해야 하므로 JSON을 단순한String컬럼으로 저장하는 것보다 삽입이 더 느립니다. - 전체 객체를 읽을 때 더 느림 - 특정 필드가 아니라 전체 JSON 문서를 가져와야 한다면
JSON타입은String컬럼에서 읽는 것보다 더 느립니다. 필드 수준 쿼리를 수행하지 않는 경우에는 분리된 서브컬럼에서 객체를 다시 구성하는 오버헤드가 아무런 이점을 주지 않습니다. - 스토리지 오버헤드 - 별도의 서브컬럼을 유지하면 JSON을 하나의 문자열 값으로 저장할 때보다 구조적 오버헤드가 추가됩니다.
다음과 같은 경우 JSON 타입을 사용합니다:
- 데이터 구조가 동적이거나 예측하기 어렵고, 문서마다 키가 다릅니다
- 필드 타입이나 스키마가 시간에 따라 바뀌거나 레코드마다 다릅니다
- 구조를 미리 예측할 수 없는 JSON 객체 내의 특정 경로에 대해 쿼리, 필터링 또는 집계를 수행해야 합니다
- 사용 사례에 로그, 이벤트, 또는 스키마가 일관되지 않은 사용자 생성 콘텐츠와 같은 반정형 데이터가 포함됩니다
String 컬럼(또는 구조화된 타입)을 사용해야 하는 경우:
- 데이터 구조가 이미 정해져 있고 일관적인 경우 - 이때는 일반 컬럼이나
Tuple,Array,Dynamic,Variant타입을 대신 사용하십시오 JSON문서를 필드 수준에서 분석하지 않고, 전체를 하나의 불투명한 blob으로 저장하고 그대로 다시 읽어오기만 하는 경우- 데이터베이스 내에서 개별
JSON필드에 대해 쿼리하거나 필터링할 필요가 없는 경우 JSON이 ClickHouse 내부에서 분석 대상이 아니라 단순한 전송/저장 포맷인 경우
JSON 사용 시 고려사항과 팁
- 알려진 서브컬럼에 대해 타입을 지정해 불필요한 타입 추론을 피할 수 있도록 컬럼 정의의 힌트를 사용해 경로의 타입을 지정하세요.
- 값이 필요하지 않다면 SKIP 및 SKIP REGEXP를 사용해 경로를 건너뛰세요. 이렇게 하면 저장 공간을 줄이고 성능을 개선할 수 있습니다.
max_dynamic_paths를 너무 크게 설정하지 마세요. 값이 너무 크면 리소스 사용량이 늘고 효율이 떨어집니다. 일반적으로 10,000 미만으로 유지하는 것이 좋습니다.
타입 힌트타입 힌트는 단순히 불필요한 타입 추론을 피하는 방법에 그치지 않습니다. 저장 및 처리 과정의 간접 단계를 아예 없애 줍니다. 타입 힌트가 있는 JSON 경로는 항상 일반 컬럼과 같은 방식으로 저장되므로 discriminator columns이나 쿼리 시점의 동적 판별이 필요하지 않습니다. 즉, 타입 힌트가 잘 정의되어 있으면 중첩된 JSON 필드도 처음부터 최상위 필드로 모델링한 것과 동일한 성능과 효율을 얻을 수 있습니다. 따라서 대부분 일관적이면서도 JSON의 유연성이 여전히 필요한 데이터셋에서는 스키마나 수집 파이프라인을 재구성하지 않고도 성능을 유지할 수 있는 실용적인 방법이 됩니다.
고급 기능
- JSON 컬럼은 다른 컬럼과 마찬가지로 프라이머리 키(primary key)에 사용할 수 있습니다. 하위 컬럼에는 코덱을 지정할 수 없습니다.
JSONAllPathsWithTypes()및JSONDynamicPaths()와 같은 함수를 통해 내부 검사를 수행할 수 있습니다..^구문을 사용해 중첩된 하위 객체를 읽을 수 있습니다.- 쿼리 구문은 표준 SQL과 다를 수 있으며, 중첩 필드에는 특별한 형 변환이나 연산자가 필요할 수 있습니다.
예시
tags라는 또 다른 컬럼이 추가되었다고 가정해 보겠습니다. 이것이 단순한 문자열 목록이라면 Array(String)로 모델링할 수 있지만, 여기서는 서로 다른 타입이 섞인 임의의 태그 구조를 추가할 수 있다고 가정하겠습니다(score는 문자열일 수도 있고 정수일 수도 있습니다). 수정된 JSON 문서는 다음과 같습니다:
tags 컬럼만 추가할 수 있습니다. 아래에 두 가지 예시를 모두 제공합니다:
JSON 정의에서
update_date 컬럼에 대한 type hint를 제공합니다. update_date를 정렬/프라이머리 키(primary key)에 사용하기 때문입니다. 이렇게 하면 ClickHouse가 이 컬럼이 NULL이 될 수 없음을 알 수 있고, 어떤 update_date 서브컬럼을 사용해야 하는지도 정확히 판단할 수 있습니다(타입마다 여러 개가 있을 수 있으므로, 그렇지 않으면 모호해집니다).JSONAllPathsWithTypes 함수와 PrettyJSONEachRow 출력 형식을 사용해 이후 추론된 스키마를 확인할 수 있습니다:
tags 컬럼을 사용해 이를 모델링할 수도 있습니다. 일반적으로는 이 방법이 더 선호되며, ClickHouse에서 필요한 추론을 최소화할 수 있습니다:
tags 서브컬럼의 타입을 추론할 수 있습니다.