clickhouse-best-practices について
スキーマ、クエリ、データ挿入を確認する開発者向けの ClickHouse ガイドスキルです。適用ルールを優先し、回答で参照元を示します。
主な機能
- 01スキーマ・クエリ・挿入の28ルール
- 02優先順位に沿ったルール確認と引用
- 03スキーマとマイグレーションの手順
- 04クエリと挿入のチェックリスト
活用シーン
- 01ClickHouse のテーブルスキーマとマイグレーションの確認
- 02クエリと JOIN 戦略の最適化
- 03挿入や変更のパターンの評価
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ langfuse/ langfuse --skill clickhouse-best-practicesインストール後、Claude Code で/clickhouse-best-practicesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdrules/_sections.md_template.mdinsert-async-small-batches.mdinsert-batch-size.mdinsert-format-native.mdinsert-mutation-avoid-delete.mdinsert-mutation-avoid-update.mdinsert-optimize-avoid-final.mdquery-index-skipping-indices.mdquery-join-choose-algorithm.mdquery-join-consider-alternatives.mdquery-join-filter-before.mdquery-join-null-handling.mdquery-join-use-any.mdquery-mv-incremental.mdquery-mv-refreshable.mdschema-json-when-to-use.mdschema-partition-lifecycle.mdschema-partition-low-cardinality.mdschema-partition-query-tradeoffs.mdschema-partition-start-without.mdschema-pk-cardinality-order.mdschema-pk-filter-on-orderby.mdschema-pk-plan-before-creation.mdschema-pk-prioritize-filters.mdschema-types-avoid-nullable.mdschema-types-enum.mdschema-types-lowcardinality.mdschema-types-minimize-bitwidth.md詳細
ClickHouse のベストプラクティス
スキーマ設計、クエリ最適化、データ取り込みに関する ClickHouse の包括的なガイダンスです。影響度の高い順に優先順位付けされた、3 つの主なカテゴリ(スキーマ、クエリ、挿入)にわたる 28 のルールが含まれています。
公式ドキュメント: ClickHouse のベストプラクティス
重要:このスキルの適用方法
ClickHouse に関する質問に回答する前に、次の優先順位に従ってください。
rules/ディレクトリで該当するルールを確認します- ルールがある場合: そのルールを適用し、「
rule-nameによると…」のように回答内で引用します - ルールがない場合: LLM の ClickHouse に関する知識を使用するか、ドキュメントを検索します
- 確信が持てない場合: 最新のベストプラクティスをウェブ検索します
- 必ず出典を引用します: ルール名、「一般的な ClickHouse のガイダンス」、または URL
ルールを優先する理由: ClickHouse には、一般的なデータベースの直感では誤解を招く可能性のある、列指向ストレージ、疎インデックス、マージツリーの仕組みなど、固有の動作があります。ルールには、検証済みの ClickHouse 固有のガイダンスがまとめられています。
Langfuse 固有のルール
eventsテーブルに対するクエリには、packages/shared/src/server/queries/clickhouse-sql/event-query-builder.tsを使用してください。クエリビルダーで表現できないことを確認してからでなければ、eventsの SQL を手書きしないでください。eventsテーブルではFINALを使用しないでください。このテーブルはFINALが不要な設計になっており、このキーワードはパフォーマンスを低下させます。- ClickHouse クエリのアトリビューションは、
packages/shared/src/server/clickhouse/queryTags.tsの JSON としてsystem.query_log.log_commentに保存されます。JSONExtractString(log_comment, 'surface')、JSONExtractString(log_comment, 'route')、およびJSONExtractString(log_comment, 'projectId')で解析してください。surfaceの既知の値はtrpc、publicapi、worker、mcp、およびunknownです。ClickhouseWriter による挿入では、projectId = "MULTI_PROJECT"が使用されます。 - クエリのアトリビューションは、OpenTelemetry の baggage を通じて伝播します。
エントリーポイントでは、
packages/shared/src/server/headerPropagation.tsのcontextWithLangfuseProps(...)を呼び出し、ClickHouse のsurface、 任意のroute、および任意のprojectIdを設定します。その後、 ClickHouse のリポジトリレイヤーが baggage をnormalizeClickHouseQueryTags(...)で読み取り、log_commentに書き込みます。 すべてのリポジトリ呼び出しにタグを渡すのではなく、エントリーポイントで アトリビューションを設定することを優先してください。 packages/shared/clickhouse/migrations/canonical/**は、クラスター構成と 非クラスター構成のインストール向けにレンダリングされる、唯一の正規テンプレート ツリーです。クラスターに対応するすべての DDL の位置に{CLICKHOUSE_CLUSTER_CLAUSE}を配置してください。{CLICKHOUSE_REPLICATION_PREFIX}は、モードによって意図的に異なるエンジンに 限って使用してください。どちらのモードでも意図的に非レプリケーションのままにする テーブルもあります。- 新しい正規マイグレーション内のすべてのメタデータ
ALTER(ADD/DROP/MODIFY COLUMN、ADD/DROP INDEX)には{CLICKHOUSE_CLUSTERED_ONLY: SETTINGS alter_sync = 2}を含め、 ミューテーションを作成するすべてのALTER(MATERIALIZE …、UPDATE、DELETE)には{CLICKHOUSE_CLUSTERED_ONLY: SETTINGS mutations_sync = 2}を 含める必要があります。ALTERが 1 つだけ含まれるファイルにも適用されます。 競合はファイル内ではなく、マイグレーションファイル間で発生するためです。alter_syncのデフォルトは1なので、開始レプリカが Keeper 内のテーブルの メタデータバージョンを更新すると、ステートメントはすぐに返ります。その後、 golang-migrate は直ちに次のファイルを開き、その最初のテーブルへのALTERが、 まだ古いバージョンのレプリカに到達する可能性があります。ClickHouse はその ステートメントをキューに入れることを拒否し、レプリカのメタデータバージョンが 共通メタデータバージョンより古いため、code 517で実行全体を中止します。mutations_syncはalter_syncの代わりにはならない点に注意してください。 前者はミューテーションの完了タイミングを制御するもので、メタデータの伝播は制御 しません。レンダラーは、非クラスターのMergeTreeマイグレーションではこれらの フラグメントを省略します。意図的にモードごとの差異を設ける場合に限り、{CLICKHOUSE_UNCLUSTERED_ONLY:...}を使用してください。既存の出力を正規化する 目的で、すでにリリース済みのマイグレーションに同期設定を後付けしないでください。 履歴上の互換性テストでは、既存の出力が意図的に保護されています。 - ClickHouse のマイグレーションでは、
CREATE OR REPLACE VIEW(およびCREATE OR REPLACE TABLE/EXCHANGE TABLES)を使用しないでください。 アトミックな置き換えにはrenameat2のファイルシステムサポートが必要ですが、 NFS ベースのセルフホスト環境(例:AWS EFS 上の ClickHouse データ)にはそれが ありません。その場合、マイグレーションが失敗し、起動時にデプロイが中止されます (GitHub issue #14906)。通常のビューを再定義する場合は、同じマイグレーション ファイル内で 2 つのステートメントを使用してください。最初にDROP VIEW IF EXISTS <name> {CLICKHOUSE_CLUSTER_CLAUSE};を実行し、次にCREATE VIEW <name> {CLICKHOUSE_CLUSTER_CLAUSE} AS …を実行します。 マイグレーションランナーはx-multi-statement=trueを渡し、golang-migrate は SQL を解析せずにファイルを;で分割するため、コメントや文字列リテラルに セミコロンを含めないでください。ダーティ状態で一部だけ適用されたマイグレーションをmigrate forceの後に再実行できるよう、すべてのステートメントを冪等に保ってください (IF EXISTS/IF NOT EXISTS)。ビューの削除から作成までの間にビューを参照すると 一時的に失敗しますが、analytics_*エクスポートビューでは許容されます。そのため、 通常のビューをプロダクトのホットパスで使用しないでください。 - ライブの挿入を受け取るソーステーブルを持つマテリアライズドビューを削除して
作り直さないでください。
DROPとCREATEの間に挿入された行はすべて、 ターゲットテーブルに反映されないまま、永久に失われます。MV の SELECT を変更する 場合は、ALTER TABLE <mv> {CLICKHOUSE_CLUSTER_CLAUSE} MODIFY QUERY <select>を 使用してください。これにより、取り込みを中断せずに変換処理が切り替わります。 変更によって列が追加される場合は、まずターゲットテーブル(複数ある場合はすべて)にALTER(ADD COLUMN IF NOT EXISTS …)を適用し、その後にMODIFY QUERYを 実行してください。ホストが、新しい MV クエリを、ターゲットレプリカに新しい列が 追加される前に適用しないよう、これらのターゲットテーブルへのALTERには クラスター構成のみで使用するalter_syncテンプレートフラグメントを含める必要が あります。MODIFY QUERYを使用できるのはTOテーブル MV のみです (Langfuse の MV はすべてTOを使用します)。
レビュープロセス
スキーマレビュー(CREATE TABLE、ALTER TABLE)
次のルールファイルを順番に確認してください:
rules/schema-pk-plan-before-creation.md- ORDER BY は変更できませんrules/schema-pk-cardinality-order.md- キー内のカラム順序rules/schema-pk-prioritize-filters.md- フィルターカラムの追加rules/schema-types-native-types.md- 適切な型の選択rules/schema-types-minimize-bitwidth.md- 数値型のサイズ設定rules/schema-types-lowcardinality.md- LowCardinality の使用rules/schema-types-avoid-nullable.md- Nullable と DEFAULT の使い分けrules/schema-partition-low-cardinality.md- パーティション数の制限rules/schema-partition-lifecycle.md- パーティショニングの目的
次の点を確認してください:
- PRIMARY KEY / ORDER BY のカラム順序(カーディナリティの低い順)
- データ型が実際のデータ範囲に合っている
- 適切な文字列カラムに LowCardinality が適用されている
- パーティションキーのカーディナリティが制限されている(100~1,000 個の値)
- ReplacingMergeTree を使用する場合、バージョンカラムがある
- 新しい正規のマイグレーションに含まれるすべてのメタデータ ALTER に
{CLICKHOUSE_CLUSTERED_ONLY: SETTINGS alter_sync = 2}が含まれている(ALTER が 1 つだけのファイルも対象です。問題が発生するのは次のマイグレーションファイルであるためです)。また、すべてのMATERIALIZE …/UPDATE/DELETEに対応するmutations_syncフラグメントが含まれている。mutations_syncはalter_syncの代わりにはなりません。すでに配布済みのマイグレーション出力を標準化しないでください。両方のレンダリングモードでprepareMigrations.test.tsが成功する - マイグレーションに
CREATE OR REPLACE VIEW/TABLEまたはEXCHANGE TABLESがない(NFS/EFS のセルフホストで問題が発生します)。通常のビューは、同じファイル内でDROP VIEW IF EXISTSとCREATE VIEWを使って再定義する - ソーステーブルへの INSERT が行われている間は、マテリアライズドビューを削除して再作成しない。SELECT の変更は、対象テーブルの
ALTERの後にALTER TABLE <mv> MODIFY QUERYで行う
クエリレビュー(SELECT、JOIN、集約)
次のルールファイルを確認してください:
rules/query-join-choose-algorithm.md- アルゴリズムの選択rules/query-join-filter-before.md- JOIN 前のフィルタリングrules/query-join-use-any.md- ANY と通常の JOIN の使い分けrules/query-index-skipping-indices.md- セカンダリインデックスの使用rules/schema-pk-filter-on-orderby.md- ORDER BY とフィルターの整合性
次の点を確認してください:
- フィルターで ORDER BY のプレフィックスカラムを使用している
- JOIN では、結合後ではなく結合前にテーブルをフィルタリングしている
- テーブルサイズに適した JOIN アルゴリズムを使用している
- ORDER BY に含まれないフィルターカラムにスキップインデックスを使用している
INSERT 戦略レビュー(データの取り込み、更新、削除)
次のルールファイルを確認してください:
rules/insert-batch-size.md- バッチサイズの要件rules/insert-mutation-avoid-update.md- UPDATE の代替手段rules/insert-mutation-avoid-delete.md- DELETE の代替手段rules/insert-async-small-batches.md- 非同期 INSERT の使用rules/insert-optimize-avoid-final.md- OPTIMIZE TABLE のリスク
次の点を確認してください:
- INSERT 1 回あたりのバッチサイズが 10K~100K 行である
- 頻繁な変更に ALTER TABLE UPDATE を使用していない
- 更新パターンに ReplacingMergeTree または CollapsingMergeTree を使用している
- 少量のバッチを高頻度で挿入する場合、非同期 INSERT が有効になっている
出力形式
次の形式で回答してください:
## Rules Checked
- `rule-name-1` - Compliant / Violation found
- `rule-name-2` - Compliant / Violation found
...
## Findings
### Violations
- **`rule-name`**: Description of the issue
- Current: [what the code does]
- Required: [what it should do]
- Fix: [specific correction]
### Compliant
- `rule-name`: Brief note on why it's correct
## Recommendations
[Prioritized list of changes, citing rules]
優先度別のルールカテゴリ
| 優先度 | カテゴリ | 影響 | プレフィックス | ルール数 |
|---|---|---|---|---|
| 1 | 主キーの選択 | 重大 | schema-pk- | 4 |
| 2 | データ型の選択 | 重大 | schema-types- | 5 |
| 3 | JOIN の最適化 | 重大 | query-join- | 5 |
| 4 | 挿入のバッチ処理 | 重大 | insert-batch- | 1 |
| 5 | ミューテーションの回避 | 重大 | insert-mutation- | 2 |
| 6 | パーティション戦略 | 高 | schema-partition- | 4 |
| 7 | スキッピングインデックス | 高 | query-index- | 1 |
| 8 | マテリアライズドビュー | 高 | query-mv- | 2 |
| 9 | 非同期挿入 | 高 | insert-async- | 2 |
| 10 | OPTIMIZE の回避 | 高 | insert-optimize- | 1 |
| 11 | JSON の使用 | 中 | schema-json- | 1 |
クイックリファレンス
スキーマ設計 - 主キー(重大)
schema-pk-plan-before-creation- テーブル作成前に ORDER BY を計画します(変更不可)schema-pk-cardinality-order- カーディナリティの低い列から高い列の順に並べますschema-pk-prioritize-filters- 頻繁にフィルタリングされる列を含めますschema-pk-filter-on-orderby- クエリのフィルターでは ORDER BY のプレフィックスを使用する必要があります
スキーマ設計 - データ型(重大)
schema-types-native-types- すべてに String を使わず、ネイティブ型を使用しますschema-types-minimize-bitwidth- 値に適合する最小の数値型を使用しますschema-types-lowcardinality- 一意な文字列が 10K 未満の場合は LowCardinality を使用しますschema-types-enum- 値の範囲が有限で検証が必要な場合は Enum を使用しますschema-types-avoid-nullable- Nullable を避け、代わりに DEFAULT を使用します
スキーマ設計 - パーティション(高)
schema-partition-low-cardinality- パーティション数を 100~1,000 に保ちますschema-partition-lifecycle- クエリではなく、データのライフサイクル管理にパーティショニングを使用しますschema-partition-query-tradeoffs- パーティションプルーニングのトレードオフを理解しますschema-partition-start-without- パーティショニングなしで始めることを検討します
スキーマ設計 - JSON(中)
schema-json-when-to-use- 動的なスキーマには JSON を、既知のデータには型付き列を使用します
クエリ最適化 - JOIN(重大)
query-join-choose-algorithm- テーブルのサイズに基づいてアルゴリズムを選択しますquery-join-use-any- 一致する行が 1 つだけ必要な場合は ANY JOIN を使用しますquery-join-filter-before- JOIN の前にテーブルをフィルタリングしますquery-join-consider-alternatives- JOIN の代わりに辞書や非正規化を検討しますquery-join-null-handling- デフォルト値を使用するには join_use_nulls=0 を設定します
クエリ最適化 - インデックス(高)
query-index-skipping-indices- ORDER BY に含まれない列でのフィルターにはスキッピングインデックスを使用します
クエリ最適化 - マテリアライズドビュー(高)
query-mv-incremental- リアルタイム集計にはインクリメンタル MV を使用しますquery-mv-refreshable- 複雑な JOIN には更新可能な MV を使用します
挿入戦略 - バッチ処理(重大)
insert-batch-size- INSERT ごとに 10K~100K 行をバッチ処理します
挿入戦略 - 非同期(高)
insert-async-small-batches- 頻度の高い小規模バッチには非同期挿入を使用しますinsert-format-native- 最高のパフォーマンスを得るにはネイティブ形式を使用します
挿入戦略 - ミューテーション(重大)
insert-mutation-avoid-update- ALTER UPDATE の代わりに ReplacingMergeTree を使用しますinsert-mutation-avoid-delete- 軽量 DELETE または DROP PARTITION を使用します
挿入戦略 - 最適化(高)
insert-optimize-avoid-final- バックグラウンドマージに任せます
適用するタイミング
以下に該当する場合に、このスキルが有効になります。
CREATE TABLE文ALTER TABLEによる変更ORDER BYまたはPRIMARY KEYに関する議論- データ型の選択に関する質問
- 遅いクエリのトラブルシューティング
- JOIN の最適化依頼
- データ取り込みパイプラインの設計
- 更新/削除戦略に関する質問
- ReplacingMergeTree またはその他の特殊なエンジンの使用
- パーティション戦略の決定
ルールファイルの構成
rules/ 内の各ルールファイルには、以下が含まれます。
- YAML フロントマター:タイトル、影響レベル、タグ
- 簡単な説明:このルールが重要な理由
- 誤った例:アンチパターンとその説明
- 正しい例:ベストプラクティスとその説明
- 追加のコンテキスト:トレードオフ、適用するタイミング、参考資料
コメント
Markdown 対応