가이드를 찾고 계신가요?
예시, 고급 기능, JSON 타입 사용 시 고려 사항은 JSON 모범 사례 가이드에서 확인하십시오.
JSON 타입은 JavaScript Object Notation(JSON) 문서를 단일 컬럼에 저장합니다.
ClickHouse Open-Source에서는 버전 25.3부터 JSON 데이터 타입이 프로덕션 환경에서 사용 가능한 것으로 표시됩니다. 이전 버전에서는 이 타입을 프로덕션 환경에서 사용하는 것을 권장하지 않습니다.
JSON 타입 컬럼을 선언하려면 다음 구문을 사용할 수 있습니다:
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 만들기
JSON을 만드는 다양한 방법을 살펴보겠습니다.
테이블 컬럼 정의에서 JSON 사용하기
Query (Example 1)
Response (Example 1)
Query (Example 2)
Response (Example 2)
::JSON으로 CAST 사용하기
::JSON을 사용하면 다양한 타입을 CAST할 수 있습니다.
String을 JSON으로 CAST
Query
Response
Tuple을 JSON으로 CAST
Query
Response
Map을 JSON으로 CAST
Query
Response
JSON 경로는 평탄화된 형태로 저장됩니다. 즉, 반환 결과는 다음과 같습니다.다음이 아닙니다:
a.b.c와 같은 경로로부터 JSON 객체를 포맷할 때
이를 { "a.b.c" : ... }로 구성해야 하는지, 아니면 { "a": { "b": { "c": ... } } }로 구성해야 하는지 판단할 수 없습니다.
현재 구현에서는 항상 후자로 간주합니다.예시:쿼리
응답
JSON 경로를 서브컬럼으로 읽기
JSON 타입은 모든 경로를 각각의 서브컬럼으로 읽을 수 있습니다.
JSON 타입 선언에서 요청한 경로의 타입을 지정하지 않으면,
해당 경로의 서브컬럼은 항상 Dynamic 타입이 됩니다.
예시:
Query
Response
Query (Reading JSON paths as sub-columns)
Response (Reading JSON paths as sub-columns)
getSubcolumn 함수를 사용해 JSON 타입에서 서브컬럼을 읽어올 수 있습니다:
Query
Response
NULL 값으로 채워집니다:
Query
Response
Query
Response
a.b의 유형은 JSON 타입 선언에서 지정한 대로 UInt32이고,
다른 모든 서브컬럼의 유형은 Dynamic입니다.
또한 특수 구문 json.some.path.:TypeName을 사용하면 Dynamic 유형의 서브컬럼도 읽을 수 있습니다:
Query
Response
Dynamic 서브컬럼은 어떤 데이터 타입으로도 CAST할 수 있습니다. 이 경우 Dynamic 내부의 타입을 요청한 타입으로 CAST할 수 없으면 예외가 발생합니다:
Query
Response
Query
Response
Compact MergeTree 파트에서 서브컬럼을 효율적으로 읽으려면 MergeTree 설정 write_marks_for_substreams_in_compact_parts가 활성화되어 있어야 합니다.
JSON 하위 객체를 서브컬럼으로 읽기
JSON 타입은 특수 구문 json.^some.path를 사용해 중첩된 객체를 JSON 타입의 서브컬럼으로 읽을 수 있습니다:
Query
Response
Query
Response
경로가 기본(
map) 공유 데이터에 저장되어 있으면 전체 공유 데이터 구조를 스캔해야 하므로 하위 객체 서브컬럼을 읽는 작업이 비효율적일 수 있습니다. 반면 map_with_buckets 또는 advanced 공유 데이터 직렬화를 사용하면 공유 데이터에서 서브컬럼을 읽는 작업이 고도로 최적화됩니다.JSON combined 서브컬럼 읽기
JSON 타입은 특수 구문 json.@some.path를 사용해 경로를 combined 서브컬럼으로 읽을 수 있습니다.
지정한 경로의 combined 서브컬럼은 다음을 반환합니다:
- 해당 경로에 리터럴 값이 있으면, 그 경로에 저장된 리터럴 값을
Dynamic으로 반환합니다. - 해당 경로에 리터럴 값은 없지만 중첩된 하위 경로가 있으면, 그 경로의 JSON 하위 객체를
Dynamic으로 반환합니다. - 해당 경로에 리터럴 값도 하위 경로도 없으면
NULL을 반환합니다.
json.a)과 하위 객체 서브컬럼(json.^a)을 각각 따로 쿼리하는 것보다 더 편리합니다.
다음 예시는 경로 a에 대한 3가지 서브컬럼 타입을 모두 비교합니다:
Query
Response
Query
Response
- Row 1:
a에는 리터럴42가 있습니다.json.a는 이를Dynamic(Int64)로 반환하고,json.^a는 빈 하위 객체{}를 반환하며(a아래에 중첩된 키가 없음),json.@a는 리터럴42를 반환합니다. - Row 2:
a에는 중첩된 객체가 있습니다.json.a는NULL을 반환하고(해당 경로에 리터럴 값이 없음),json.^a는 하위 객체를JSON으로 반환하며,json.@a도 하위 객체를Dynamic(JSON)로 반환합니다. - Row 3:
a가 아예 없습니다.json.a와json.@a는 모두NULL을 반환하고,json.^a는 빈{}를 반환합니다.
경로가 기본(
map) 공유 데이터에 저장된 경우, 결합 서브컬럼을 읽으려면 전체 공유 데이터 구조를 스캔해야 하므로 비효율적일 수 있습니다. map_with_buckets 또는 advanced 공유 데이터 직렬화를 사용하면 공유 데이터에서 서브컬럼을 읽는 작업이 크게 최적화됩니다.경로에 대한 타입 추론
JSON을 파싱하는 동안 ClickHouse는 각 JSON 경로에 가장 적합한 데이터 타입을 판별하려고 합니다.
이는 입력 데이터의 자동 스키마 추론과 비슷하게 동작하며,
같은 설정으로 제어됩니다:
- input_format_try_infer_dates
- input_format_try_infer_datetimes
- schema_inference_make_columns_nullable
- input_format_json_try_infer_numbers_from_strings
- input_format_json_infer_incomplete_types_as_strings
- input_format_json_read_numbers_as_strings
- input_format_json_read_bools_as_strings
- input_format_json_read_bools_as_numbers
- input_format_json_read_arrays_as_strings
- input_format_json_infer_array_of_dynamic_from_array_of_different_types
Query
Response
Query
Response
Query
Response
Query
Response
JSON 객체 배열 처리
Array(JSON) 유형으로 파싱되어, 해당 경로의 Dynamic 컬럼에 삽입됩니다.
객체 배열을 읽으려면 Dynamic 컬럼에서 하위 컬럼으로 추출할 수 있습니다:
Query
Response
Query
Response
JSON 타입의 max_dynamic_types/max_dynamic_paths 매개변수는 기본값보다 작게 줄어들었습니다.
이는 JSON 객체의 중첩 배열에서 서브컬럼 수가 걷잡을 수 없이 늘어나는 것을 방지하기 위해 필요합니다.
이제 중첩된 JSON 컬럼에서 서브컬럼을 읽어보겠습니다:
Query
Response
Array(JSON) 하위 컬럼 이름을 일일이 쓰지 않고도 됩니다.
Query
Response
[]의 개수는 배열 수준을 나타냅니다. 예를 들어 json.path[][]는 json.path.:Array(Array(JSON))로 변환됩니다.
이제 Array(JSON) 내부의 경로와 타입을 확인해 보겠습니다.
Query
Response
Array(JSON) 컬럼에서 서브컬럼을 읽어보겠습니다:
Query
Response
JSON 컬럼에서 하위 객체의 서브컬럼도 읽을 수 있습니다:
Query
Response
NULL 값을 가진 JSON 키 처리
null과 값이 없는 경우를 동일하게 간주합니다:
Query
Response
점이 포함된 JSON 키 처리
a.b와 값 42의 쌍으로 저장됩니다. JSON을 포맷할 때는 점으로 구분된 경로 파트를 기준으로 항상 중첩된 객체를 만듭니다:
Query
Response
{"a.b" : 42}는 이제 {"a" : {"b" : 42}} 형태로 포맷됩니다.
이러한 제한 때문에 다음과 같이 유효한 JSON 객체도 파싱에 실패합니다:
Query
Response
25.8부터 사용 가능). 이 경우 파싱 시 JSON 키의 모든 점은
%2E로 이스케이프되며, 포맷 시 다시 원래대로 복원됩니다.
Query
Response
Query
Response
Query
Response
json.`a.b` 은 하위 컬럼 json.a.b 와 동일하며, 이스케이프된 점이 포함된 경로는 읽지 못합니다:
Query
Response
SKIP/SKIP REGEX 섹션에서 사용하려면 힌트에서 점을 이스케이프해야 합니다:
Query
Response
Query
Response
데이터에서 JSON 타입 읽기
JSONEachRow,
TSV,
CSV,
CustomSeparated,
Values, 등)에서는 JSON 타입을 읽을 수 있습니다.
예시:
Query
Response
CSV/TSV 등의 텍스트 형식에서는 JSON이 JSON 객체를 포함하는 문자열로부터 파싱됩니다:
Query
Response
JSON 내부 동적 경로 한도에 도달하는 경우
JSON 데이터 타입은 내부적으로 제한된 수의 경로만 별도의 서브컬럼으로 저장할 수 있습니다.
기본적으로 이 한도는 1024이며, 타입 선언에서 max_dynamic_paths 매개변수를 사용해 변경할 수 있습니다.
한도에 도달하면 JSON 컬럼에 새로 삽입되는 모든 경로는 하나의 공유 데이터 구조에 저장됩니다.
이러한 경로도 여전히 서브컬럼으로 읽을 수 있지만,
효율이 다소 떨어질 수 있습니다(공유 데이터 관련 섹션 참조).
이 한도는 서로 다른 서브컬럼 수가 과도하게 늘어나 테이블을 사용할 수 없게 되는 상황을 방지하기 위해 필요합니다.
이제 몇 가지 시나리오에서 한도에 도달하면 어떤 일이 발생하는지 살펴보겠습니다.
데이터 파싱 중 제한에 도달하는 경우
JSON 객체를 파싱하는 동안 현재 데이터 블록의 제한에 도달하면
새로운 모든 경로는 공유 데이터 구조에 저장됩니다. 다음 두 가지 인트로스펙션 함수 JSONDynamicPaths, JSONSharedDataPaths를 사용할 수 있습니다:
Query
Response
e와 f.g를 삽입한 후 한도에 도달했고,
이들은 공유 데이터 구조에 삽입되었습니다.
MergeTree 테이블 엔진에서 데이터 파트를 머지하는 동안
MergeTree 테이블에서 여러 데이터 파트를 머지하는 동안, 결과 데이터 파트의 JSON 컬럼이 동적 경로 한도에 도달할 수 있으며
소스 파트의 모든 경로를 서브컬럼으로 저장하지 못할 수 있습니다.
이 경우 ClickHouse는 머지 후 어떤 경로를 서브컬럼으로 유지하고, 어떤 경로를 공유 데이터 구조에 저장할지 결정합니다.
대부분의 경우 ClickHouse는 null이 아닌 값을 가장 많이 포함한 경로를 유지하고, 가장 드문 경로를 공유 데이터 구조로 이동하려고 합니다. 다만 이는 구현에 따라 달라질 수 있습니다.
이러한 머지의 예시를 살펴보겠습니다.
먼저 JSON 컬럼이 있는 테이블을 만들고, 동적 경로 한도를 3으로 설정한 다음, 서로 다른 5개의 경로를 가진 값을 삽입하겠습니다:
Query
JSON 컬럼에 단일 경로만 포함된 별도의 데이터 파트를 생성합니다:
Query
Response
Query
Response
a, b, c는 유지하고 d와 e 경로는 공유 데이터 구조로 옮겼습니다.
이전 섹션에서 설명했듯이 max_dynamic_paths 제한에 도달하면 모든 새 경로는 하나의 공유 데이터 구조에 저장됩니다.
이 섹션에서는 공유 데이터 구조의 세부 사항과 이 구조에서 경로 서브컬럼을 읽는 방법을 살펴봅니다.
JSON 컬럼의 내용을 검사하는 데 사용되는 함수에 관한 자세한 내용은 “인트로스펙션 함수” 섹션을 참조하십시오.
메모리상의 공유 데이터 구조는 평탄화된 JSON 경로를 바이너리로 인코딩된 값에 매핑해 저장하는 Map(String, String) 유형의 서브컬럼일 뿐입니다.
여기서 경로 서브컬럼을 추출하려면 이 Map 컬럼의 모든 행을 순회하면서 요청된 경로와 해당 값을 찾기만 하면 됩니다.
MergeTree 테이블에서는 모든 데이터를 디스크(로컬 또는 원격)에 저장하는 데이터 파트에 보관합니다. 또한 디스크의 데이터는 메모리와 다른 방식으로 저장될 수 있습니다.
현재 MergeTree 데이터 파트에는 3가지 공유 데이터 구조 직렬화 방식이 있습니다: map, map_with_buckets
및 advanced입니다.
직렬화 버전은 MergeTree
설정 object_shared_data_serialization_version
및 object_shared_data_serialization_version_for_zero_level_parts로
제어됩니다
(제로 수준 파트는 테이블에 데이터를 삽입할 때 생성되는 파트이며, 머지 과정에서 생성되는 파트는 더 높은 수준을 가집니다).
참고: 공유 데이터 구조 직렬화 변경은 v3 object serialization version에서만
지원됩니다
map 직렬화 버전에서는 공유 데이터가 메모리에 저장되는 것과 동일하게 Map(String, String) 타입의 단일 컬럼으로 직렬화됩니다. 이 직렬화 방식에서 경로 서브컬럼을 읽으려면 ClickHouse가 전체 Map 컬럼을 읽은 다음
메모리에서 요청된 경로를 추출합니다.
이 직렬화 방식은 데이터를 쓰고 전체 JSON 컬럼을 읽을 때는 효율적이지만, 경로 서브컬럼을 읽을 때는 효율적이지 않습니다.
map_with_buckets serialization version에서는 shared data가 Map(String, String) 유형의 N개 컬럼(“버킷”)으로 직렬화됩니다.
각 버킷에는 경로의 부분 집합만 포함됩니다. 이 직렬화 유형에서 경로 서브컬럼을 읽으려면 ClickHouse는
단일 버킷에서 전체 Map 컬럼을 읽은 다음, 메모리에서 요청된 경로를 추출합니다.
이 직렬화는 데이터를 쓰거나 전체 JSON 컬럼을 읽을 때는 효율이 떨어지지만, 경로 서브컬럼을 읽을 때는 더 효율적입니다.
필요한 버킷에서만 데이터를 읽기 때문입니다.
버킷 수 N은 MergeTree 설정 object_shared_data_buckets_for_compact_part (기본값 8)
및 object_shared_data_buckets_for_wide_part (기본값 32)로 제어됩니다.
두 설정의 허용 최대값은 모두 256입니다.
advanced 직렬화 버전에서는 공유 데이터를, 요청한 경로의 데이터만 읽을 수 있도록 추가 정보를 저장해 경로 서브컬럼 읽기 성능을 극대화하는 특수한 데이터 구조로 직렬화합니다.
또한 이 직렬화는 버킷도 지원하므로 각 버킷에는 경로의 일부 집합만 포함됩니다.
이 직렬화는 데이터 쓰기에는 상당히 비효율적이므로 zero-level 파트에는 사용을 권장하지 않습니다. 전체 JSON 컬럼을 읽는 효율도 map 직렬화보다 약간 떨어지지만, 경로 서브컬럼을 읽는 데에는 매우 효율적입니다.
참고: 데이터 구조 내부에 추가 정보를 일부 저장하므로, 이 직렬화를 사용하면 map 및 map_with_buckets 직렬화보다 디스크 저장 크기가 더 커집니다.
새로운 공유 데이터 직렬화에 대한 더 자세한 개요와 구현 세부 정보는 블로그 글을 참조하십시오.
MergeTree 파트 내 JSON의 동적 경로 수 제어하기
max_dynamic_paths 매개변수를 사용하는 것입니다.
하지만 기존 컬럼의 max_dynamic_paths를 변경하려면 ALTER TABLE <table> MODIFY COLUMN <column> JSON(max_dynamic_paths=K)를 실행해야 하며, 그러면 기존의 모든 파트를 재작성하는 백그라운드 뮤테이션이 시작됩니다.
이러한 뮤테이션은 매우 부담이 클 수 있으며 완료될 때까지 서버 성능에 영향을 줄 수 있습니다. 이를 피하려면 다음 3가지 설정을 사용해 새 데이터 파트에 대해 MergeTree 테이블의 동적 경로 한도를 변경할 수 있습니다.
merge_max_dynamic_subcolumns_in_wide_part- Wide 데이터 파트로 머지하는 동안 각 JSON 컬럼의 동적 서브컬럼 수를 제한하는 MergeTree 설정입니다.merge_max_dynamic_subcolumns_in_compact_part- Compact 데이터 파트로 머지하는 동안 각 JSON 컬럼의 동적 서브컬럼 수를 제한하는 MergeTree 설정입니다.max_dynamic_subcolumns_in_json_type_parsing- JSON 데이터를 JSON 컬럼으로 파싱하는 동안 각 JSON 컬럼의 동적 서브컬럼 수를 제한하는 세션 설정입니다.
max_dynamic_paths 매개변수에 지정된 값을 초과할 수 없습니다.
인트로스펙션 함수
JSONAllPathsJSONAllPathsWithTypesJSONAllValuesJSONDynamicPathsJSONDynamicPathsWithTypesJSONSharedDataPathsJSONSharedDataPathsWithTypesdistinctDynamicTypesdistinctJSONPaths and distinctJSONPathsAndTypes
2020-01-01 날짜의 GH Archive 데이터셋 내용을 살펴보겠습니다:
Query
Response
Query
Response
ALTER MODIFY COLUMN으로 JSON 타입으로 변경하기
JSON 타입으로 변경할 수 있습니다. 현재는 String 타입에서만 ALTER를 지원합니다.
예시
Query
Response
Lazy Type Hints (실험적)
이 기능은 실험 단계이며, 설정
allow_experimental_json_lazy_type_hints이 활성화되어 있어야 합니다.ALTER TABLE ... MODIFY COLUMN을 사용해 JSON 컬럼에 type hint를 추가하거나 수정하면, ClickHouse는 일반적으로 새 type hint를 구체화하기 위해 모든 데이터 파트를 재작성합니다. 대규모 이력 데이터(수백 TB)를 보유한 테이블에서는 이 작업 비용이 매우 클 수 있습니다.
Lazy type hints를 사용하면 기존 데이터를 재작성하지 않고 메타데이터만 변경하는 방식으로 type hint를 추가할 수 있습니다.
- 기존 파트: type hint는
Dynamic에서 힌트된 유형으로 CAST하여 쿼리 시점에 적용됩니다. - 새 파트: type hint는
INSERT작업 중에 구체화됩니다. - 머지: 파트가 머지될 때 type hint가 구체화됩니다.
Lazy Type Hints 활성화
예시
Query
Response
뮤테이션이 발생하지 않았는지 확인하기
system.mutations 테이블을 확인하면 ALTER가 뮤테이션 없이 완료되었는지 확인할 수 있습니다:
타입 힌트 구체화하기
- 백그라운드 머지를 기다립니다: ClickHouse는 파트가 머지될 때 타입 힌트를 자동으로 구체화합니다
- 머지를 강제합니다:
OPTIMIZE TABLE test_lazy FINAL을 사용하여 모든 파트를 즉시 머지합니다 - 파트를 재작성합니다:
ALTER TABLE test_lazy REWRITE PARTS를 사용하여 새 메타데이터로 파트를 재작성합니다
제한 사항
- 이 기능은 실험적이며 향후 버전에서 변경될 수 있습니다
- 쿼리 시점의 타입 변환은 미리 구체화된 타입에 비해 상당한 성능 오버헤드를 유발할 수 있으며, 특히 큰 JSON 객체에서 그렇습니다
- 이 기능은
typed_paths(type hints)를 수정할 때만 적용되며,max_dynamic_paths,SKIP,SKIP REGEXP같은 다른 JSON 매개변수는 여전히 뮤테이션이 필요합니다
JSON 타입 값 간 비교
Query
Response
Variant 데이터 타입의 비교 규칙에 따라 비교됩니다.
JSON용 데이터 스키핑 인덱스
JSON 컬럼에 다음 3가지 방식으로 사용할 수 있습니다:
- 특정 서브컬럼에 대한 인덱스 — 일반 컬럼과 마찬가지로, 알려진 JSON 경로에 표준 스킵 인덱스를 생성합니다. 이렇게 하면 해당 경로의 값이 인덱싱됩니다.
JSONAllPaths를 사용하는 경로 기반 인덱스 — 각 그래뉼에 존재하는 경로 집합을 인덱싱하여, 질의한 경로를 포함할 수 없는 그래뉼을 건너뛸 수 있습니다.JSONAllValues를 사용하는 값 기반 인덱스 — 텍스트 인덱스를 사용해 모든 JSON 경로 전반의 모든 값을 인덱싱함으로써, 단일 인덱스로 모든 JSON 서브컬럼에 대한 전문 검색을 가속화합니다.
특정 서브컬럼에 대한 인덱스
minmax, set, bloom_filter, tokenbf_v1, ngrambf_v1 등).
인덱스 표현식에서 JSON 서브컬럼을 참조하는 방법은 두 가지입니다:
- JSON 타입 힌트에 선언된 타입이 지정된 경로 — 이름으로 직접 접근합니다:
json.a. - 명시적 캐스트를 사용하는 동적 경로 —
::캐스트 구문을 사용합니다:json.b::String.
json.a || json.b::String처럼 여러 서브컬럼을 결합한 표현식도 사용할 수 있습니다.
예시
Query
data.sensor_id의 minmax 인덱스는 스캔 범위를 일치하는 그래뉼로 좁힙니다:
Query
Response
data.location::String cast 서브컬럼의 bloom_filter 인덱스도 동작합니다:
Query
Response
JSONAllPaths를 사용한 경로 기반 인덱스
JSONAllPaths 함수를 사용하면 JSON 컬럼에도 데이터 스키핑 인덱스를 생성할 수 있습니다.
이는 mapKeys를 사용해 Map 컬럼에 스킵 인덱스를 생성하는 방식과 유사합니다 — 인덱스는 각 그래뉼에 존재하는 JSON 경로 집합을 저장하고, 이를 사용해 쿼리한 경로를 포함할 수 없는 그래뉼을 건너뜁니다.
지원되는 인덱스 타입
JSONAllPaths는 다음 스킵 인덱스 타입과 함께 사용할 수 있습니다.
bloom_filter—equals,in,IS NOT NULL을 지원합니다.tokenbf_v1—equals와IS NOT NULL을 지원합니다.ngrambf_v1—equals와IS NOT NULL을 지원합니다.text(inverted index) —equals,in,IS NOT NULL을 지원합니다.
예시
Query
EXPLAIN indexes = 1을 사용해 스킵 인덱스가 사용되는지 확인할 수 있습니다. 경로가 하나의 파트에만 존재하면 인덱스가 다른 파트는 건너뜁니다:
Query
Response
Query
Response
IS NOT NULL도 인덱스를 사용합니다 — 경로가 없는 그래뉼은 건너뜁니다(값이 NULL이기 때문입니다):
Query
Response
작동 방식
JSONAllPaths(json_column) 표현식은 JSON 값에 존재하는 모든 경로가 담긴 Array(String)을 생성합니다.
스킵 인덱스는 이러한 경로 문자열을 해당 데이터 구조(블룸 필터 또는 inverted index)에 저장합니다.
쿼리에서 json.some.path를 기준으로 필터링하면, 인덱스는 각 그래뉼마다 "some.path" 문자열이 인덱스에 있는지 확인하고, 없으면 해당 그래뉼을 건너뜁니다.
누락된 경로에 대한 안전성
Dynamic유형(예:json.path)과널 허용서브컬럼(예:json.path.:Int64)은NULL로 평가됩니다.NULL과의 비교는 항상 false를 반환하므로 스키핑은 안전합니다.널 허용이 아닌 CAST 표현식은 해당 유형의 기본값으로 평가됩니다(예:json.path::Int64는 경로가 없으면0이 됨). 이 경우 스키핑은 비교값이 기본값과 다를 때만 안전합니다. 인덱스가 이 차이를 자동으로 처리합니다.
JSONAllValues를 사용한 전문 검색
JSONAllValues 함수를 통해 JSON 컬럼에 대한 전문 검색을 가속화하는 데 사용할 수 있습니다.
JSONAllValues는 JSON 컬럼의 모든 값을 Array(String)으로 반환하며, 이 값들은 텍스트 인덱스로 인덱싱할 수 있습니다.
JSONAllValues(json_column)에 단일 인덱스를 생성하면 모든 JSON 경로를 포괄할 수 있으므로, 각 경로마다 별도의 인덱스를 만들지 않고도 모든 서브컬럼에서 전문 검색을 수행할 수 있습니다.
자세한 내용과 예시는 텍스트 인덱스 문서의 JSONAllValues를 사용한 값 기반 인덱스를 참조하십시오.
JSON 타입을 더 효과적으로 사용하기 위한 팁
JSON 컬럼을 생성하고 데이터를 로드하기 전에 다음 팁을 고려하십시오:
- 데이터를 살펴보고 가능한 한 많은 경로 힌트(path hint)와 타입을 지정하십시오. 이렇게 하면 저장 및 읽기 효율이 훨씬 높아집니다.
- 어떤 경로가 필요하고 어떤 경로가 전혀 필요하지 않은지 미리 판단하십시오. 필요하지 않은 경로는
SKIP섹션에 지정하고, 필요하면SKIP REGEXP섹션에도 지정하십시오. 이렇게 하면 저장 효율이 향상됩니다. max_dynamic_paths매개변수를 지나치게 큰 값으로 설정하지 마십시오. 저장 및 읽기 효율이 떨어질 수 있습니다. 이는 메모리, CPU 등 시스템 매개변수에 크게 좌우되지만, 일반적인 경험칙으로는 로컬 파일 시스템 스토리지에서는max_dynamic_paths를 10 000보다 크게 설정하지 않고, 원격 파일 시스템 스토리지에서는 1024보다 크게 설정하지 않는 것이 좋습니다.