Skip to main content
このガイドでは、NYC Taxi dataset に2つの最適化アプローチを適用します。まず、より適切なカラム型を選択することで、保存・処理するデータ量を削減します。次に、選択性の高いクエリでClickHouseが不要なデータをスキップできるようにする順序キーを導入します。各変更は同じベースラインを基準に測定します。この例で従う全体的なワークフローについては、クエリ最適化の概要を参照してください。

始める前に

例では、nyc_taxi.trips_small_inferred テーブルを使用します。まだ作成してデータを読み込んでいない場合は、以下を実行してください。
元のParquetファイルは約5.8 GBです。ネットワーク環境や利用可能なリソースによっては、読み込みに数分かかる場合があります。
ソースの Parquet ファイルには、約3億2,900万行が含まれています。このガイドの所要時間は単一のデプロイメントで計測したものであり、利用可能なコンピュートリソースによって異なります。同一の所要時間を期待するのではなく、ステージ間での相対的な変化を比較してください。 この方法を自身のワークロードに適用する際は、クエリやスキーマを変更する前に、遅いクエリを診断する を使用して繰り返し発生するクエリパターンを特定し、代表的な実行を選択してください。

プロセスの概要

この例は、次の 3 つの段階で進めます。
  1. 推定したスキーマに対して、独立した 3 つのワークロードクエリを実行し、ベースラインを確立します。
  2. より適切なカラム型を持つテーブルを作成して同じデータを読み込み、クエリを再実行します。
  3. 同じ最適化済みスキーマと順序キーを持つ別のテーブルを作成し、再度クエリを実行します。
スキーマと順序キーを別々の段階で変更することで、それぞれの効果を区別しやすくなります。最適化のアプローチでは、これらの変更を検討すべきタイミングと検証方法について説明しています。比較可能な測定値を収集するための詳しいガイダンスについては、クエリのボトルネックを切り分けるを参照してください。

ベースラインとなるワークロードを定義する

ワークロードの実行に使用したのと同じクライアントセッションで、リモートデータのファイルシステムキャッシュ、クエリキャッシュ、クエリ条件キャッシュを無効にします。
これらの設定は、テスト中の繰り返し実行結果を比較可能にするのに役立ちます。測定が完了したら、元の値に戻してください。
以下の3つの独立したクエリが、ベースラインワークロードを構成します。以降のステージで作成する各テーブルに対して、3つすべてを実行してください。各クエリを同等の条件下で複数回実行し、中央値などの代表的な実行時間と、読み取った行数およびピークメモリ使用量を記録してください。system.query_log からこれらの値を取得する方法を含む測定ワークフローの全体については、再現可能なベースラインを確立するを参照してください。

計算した乗車速度でフィルタリングする

このクエリでは、時速30マイルを超える乗車について距離の分布を求める前に、乗車時間と速度を計算します。

日付範囲内の乗車記録を集計する

このクエリは、2009 年第 1 四半期の乗車回数、走行距離、平均支払額を計算します。

乗客数でフィルタリング

このクエリは、乗客数が1人または2人の乗車記録の平均乗車時間を計算します。
元の測定結果は次のとおりです。 3 つのクエリはいずれも約 3 億 2,900 万行を読み取っており、これはテーブルの行数にほぼ等しい値です。このことから、ワークロードの 2 つの側面を改善できる可能性が見えてきます。まず、選択したカラムの処理コストを下げ、次にフィルターで絞り込める場合は選択する行数を減らします。

スキーマを最適化する

スキーマ推論はデータセットの調査を始める実用的な方法ですが、推論された型はワークロードで必要とされるものよりも広範囲であったり、許容範囲が広すぎたりする場合があります。推論された型が不要だと決めつけず、スキーマを変更する前にデータを確認してください。

不要な Nullable カラムを避ける

Nullable カラムでは、値に加えて null マスクも保存されます。null 値と型のデフォルト値を区別する必要がある場合は Nullable を使用しますが、値が必ず存在するカラムでは使用を避けてください。 例のスキーマで使用しているカラムの null 値をカウントします。
このデータセットで null 値を含むのは、ratecode_idmta_taxpayment_type だけです。最適化後のスキーマでは、これらのカラムには Nullable を残し、他のカラムからは削除します。

繰り返し値には LowCardinality を使用する

LowCardinality は辞書エンコーディングを使用し、繰り返し値の多いカラムのストレージ使用量と処理負荷を削減できます。適用する前に、異なる値の数を確認してください。
これら4つのカラムに含まれる異なる値の数は、行数に比べて大幅に少なくなっています。これらは LowCardinality の有力な候補ですが、ワークロードに対する効果は実際に測定する必要があります。候補を特定する際の目安としては、異なる値が約10,000個であることが有用ですが、これは固定の上限ではありません。

より適切なデータ型を選択する

必要な範囲と精度を安全に維持できる、最も小さい型を使用します。たとえば、推論された Int64Float64 を置き換える前に、数値カラムの最小値と最大値を確認します。
どちらの整数カラムも UInt8 に収まりますが、passenger_count は最大値である 255 に達します。この例では、trip_distanceFloat32、金額には Decimal32 も使用しています。このデータセット内のすべての値は対象の範囲内に収まり、この例では集計結果を比較するワークロードであるため、浮動小数点精度の低下とセント単位の金額精度を許容しています。ソース値の完全な精度が必要な場合は、より広い範囲のソース型を維持してください。この例のクエリでは秒未満の精度が不要なため、同じ UTC タイムゾーン内で、推論された DateTime64 カラムを DateTime に置き換えています。 これらの選択は、このデータセットに固有のものです。同じ変更を適用する前に、本番データに求められる範囲、精度、NULL 許容性を確認してください。

スキーマの変更を適用する

このステージでスキーマの変更を個別に測定できるよう、順序キーを指定せずにテーブルを作成します。
各ワークロードクエリで nyc_taxi.trips_small_inferrednyc_taxi.trips_small_no_pk に置き換え、3 つのクエリをすべて再実行します。元の例では、次のような代表値が得られました。 クエリが読み取る行数は同じですが、最適化されたスキーマでは、それらの行が表すデータ量が削減されます。そのため、選択するデータを変えずに、クエリの実行時間とピークメモリを改善できます。 2 つのテーブルのディスク上のサイズを比較します。
このデータセットでは、スキーマを最適化することで、圧縮後のストレージ使用量を7.38 GiBから4.89 GiBへ、約34%削減できます。

順序キーを最適化する

MergeTree ファミリーでは、順序キーによって行のディスク上での配置が決まります。ClickHouse はこの順序に基づいてスパースプライマリインデックスを構築し、クエリのフィルター条件を満たさないグラニュールをスキップします。多くのトランザクション型データベースの主キーとは異なり、一意性を保証するものではありません。 順序キーには、重要で繰り返し実行されるクエリで使用するフィルターを反映させる必要があります。カラムの順序は重要です。クエリが有用なプレフィックスでフィルタリングする場合、キーは最も効果的に機能します。カーディナリティの低いカラムは、頻繁にフィルタリングされる場合、効果的な先頭のキー要素となることがあります。また、時間ベースのワークロードでは、時間の部分が役立つことがよくあります。選択に関する詳しいガイダンスについては、主キーの選択を参照してください。 この例では、(passenger_count, pickup_datetime, dropoff_datetime) を使用します。passenger_count は異なる値が少なく、乗客数でフィルタリングする条件に使用されます。一方、pickup_datetime は日付範囲の集約に使用されます。pickup_datetime は先頭のカラムではありませんが、先頭カラムに条件が指定されていない場合でも、ClickHouse は後続のキーカラムの値を使用してデータを除外できます。一般に、順序キーの有用なプレフィックスでフィルタリングすると、より効果的に枝刈りできます。

順序キーの変更を適用する

前のステージで使用した最適化済みスキーマと同じスキーマでテーブルを作成します。変更するのは順序キーのみです。
各ワークロードクエリのテーブル名を nyc_taxi.trips_small_pk に置き換えた後、3つのクエリをすべて再実行します。

結果を比較する

元のガイドでは、3 つの段階で以下の測定値が記録されています。 スキーマを最適化するとストレージ使用量が削減され、選択した値をより低コストで処理できます。日付範囲集計では、ClickHouse が指定した日付範囲外のグラニュールをスキップできるため、順序キーによる追加の改善が最も大きくなります。乗客数フィルターでも、最初のキーカラムを条件にフィルタリングするため、読み取り行数が減少します。計算速度フィルターでは、順序キーの有効なプレフィックスではなく、pickup_datetimedropoff_datetimetrip_distance からフィルター条件が導出されるため、引き続きテーブル全体を読み取ります。 EXPLAIN indexes = 1 を使用して日付範囲集計を確認します。
ClickHouse 25.9 以降では、これらの設定により、EXPLAIN で使用された索引と、それらによって除外されたパーツおよびグラニュールが報告されます。
プライマリインデックスは、40,167 個のグラニュールのうち 5,061 個を選択します。この絞り込みにより、日付範囲の集計で処理される行数は、全体の 3 億 2,904 万行ではなく 4,146 万行になります。

ワークロードにこの手法を適用する

ご自身のワークロードにも同じ手順を適用してください。
  1. ベースラインの実行時間、読み取り行数とバイト数、ピークメモリを記録します。
  2. 選択したカラムに、不必要に幅の広い型や許容範囲の広い型が使われていないか確認します。
  3. データレイアウトを変更せずに、スキーマ変更を適用して測定します。
  4. 重要で繰り返し実行されるクエリで使用されるフィルターに基づいて、順序キーをテストします。
  5. EXPLAIN indexes = 1 で選択されるデータを比較し、同等の条件でベースラインクエリを再実行します。
この例の型や順序キーが、別のデータセットにも適しているとは限りません。これらの判断は、観測した値とクエリフィルターに基づいて行ってください。

次のステップ

スキーマやソートキーを変更しても特定されたボトルネックを解消できない場合は、最適化アプローチに戻り、projections、materialized view、データスキッピングインデックス、または事前計算を検討してください。
最終更新日 2026年8月28日