JSON 型を使用する場面
JSON 型は、動的または予測しにくい構造を持つ JSON オブジェクト内の特定のフィールドに対して、クエリ、フィルタリング、集計を行うために設計されています。これは、JSON オブジェクトを個別のサブカラムに分割することで実現されます。これにより、Map や文字列のパースなどの代替手段と比べて、読み取るデータ量を大幅に削減し、選択したフィールドに対するクエリを高速化できます。
ただし、これには重要なトレードオフがあります。
INSERTが遅くなる - JSON をサブカラムに分割し、型推論を行い、柔軟なストレージ構造を管理する必要があるため、JSON を単純なStringカラムとして保存する場合に比べて、INSERTは遅くなります。- オブジェクト全体を読み取る場合は遅くなる - 完全な JSON ドキュメント全体を取得する必要がある場合 (特定のフィールドではなく) 、
JSON型はStringカラムから読み取るよりも低速です。個別のサブカラムからオブジェクトを再構築するオーバーヘッドは、フィールドレベルのクエリを行わない場合にはメリットがありません。 - ストレージのオーバーヘッド - 個別のサブカラムを維持するため、JSON を単一の文字列値として保存する場合に比べて、構造上のオーバーヘッドが増えます。
JSON 型を使用するケース:
- データの構造が動的または予測しにくく、ドキュメントごとにキーが異なる
- フィールドの型やスキーマが時間の経過とともに変化する、またはレコードごとに異なる
- JSON オブジェクト内の特定のパスに対して、構造を事前に予測できなくてもクエリ、フィルタ、集計を行う必要がある
- ユースケースに、スキーマに一貫性のないログ、イベント、ユーザー生成コンテンツなどの半構造化データが含まれる
String カラム (または構造化型) を使用するのは次のような場合です:
- データ構造が明確で一貫している場合。この場合は、代わりに通常のカラム、
Tuple、Array、Dynamic、またはVariant型を使用します JSONドキュメントを不透明なブロブとして扱い、フィールド単位で解析せず、全体をそのまま保存・取得するだけの場合- データベース内で個々の JSON フィールドに対してクエリやフィルタを行う必要がない場合
JSONが単なる転送/保存用のフォーマットであり、ClickHouse 内では解析しない場合
JSON を使用する際の考慮事項とヒント
- 既知のサブカラムについては、不要な型推論を避けるため、カラム定義内のヒントを使ってパスの型を指定します。
- 値が不要な場合は、SKIP と SKIP REGEXPでパスをスキップし、ストレージ使用量を削減してパフォーマンスを向上させます。
max_dynamic_pathsは高くしすぎないでください。値を大きくするとリソース消費が増え、効率が低下します。目安として、10,000 未満に抑えてください。
型ヒント型ヒントは、不要な型推論を避けるためだけのものではありません。ストレージや処理における間接性そのものを完全になくします。型ヒントが付いた JSON パスは、常に従来のカラムと同様に格納されるため、discriminator columnsや、クエリ時の動的な解決を必要としません。つまり、型ヒントを適切に定義しておけば、ネストされた JSON フィールドでも、最初からトップレベルのフィールドとして定義されていたかのように、同等のパフォーマンスと効率を得られます。そのため、大部分は一貫している一方で JSON の柔軟性も活かしたいデータセットでは、スキーマや取り込みパイプラインを作り直すことなくパフォーマンスを維持するうえで、型ヒントは便利な手段になります。
高度な機能
- JSONカラムは、他のカラムと同様に主キーに使用できます。サブカラム に codecs は指定できません。
JSONAllPathsWithTypes()andJSONDynamicPaths()のような関数によるイントロスペクションをサポートしています。.^構文を使って、ネストされた sub-objects を読み取れます。- クエリ構文は標準SQLと異なる場合があり、ネストされたフィールドには特別なキャストや演算子が必要になることがあります。
例
tags という別のカラムが追加されたとします。これが単なる文字列のリストであれば、Array(String) で表現できますが、ここでは型が混在した任意のタグ構造を追加できると仮定します (score が文字列または整数になっていることに注目してください) 。修正後の JSON ドキュメントは次のとおりです。
tags カラムを追加するだけにすることもできます。以下に両方の例を示します。
update_date カラムは順序付け/主キーで使用するため、JSON 定義でその型ヒントを指定しています。これにより、ClickHouse はこのカラムが null にならないことを認識でき、さらにどの update_date サブカラムを使うべきかも判断できます (型ごとに複数存在する可能性があるため、そうしないと曖昧になります) 。JSONAllPathsWithTypes 関数と PrettyJSONEachRow 出力フォーマットを使って確認できます。
tags カラムを使ってこれを表現することもできます。一般的にはこちらのほうが好ましく、ClickHouse 側で必要となる推論を最小限に抑えられます。
tags サブカラムの型を推論できるようになりました。