始める前に
nyc_taxi.trips_small_inferred テーブルを使用します。まだ作成してデータを読み込んでいない場合は、以下を実行してください。
サンプルデータセットをセットアップする
サンプルデータセットをセットアップする
元のParquetファイルは約5.8 GBです。ネットワーク環境や利用可能なリソースによっては、読み込みに数分かかる場合があります。
プロセスの概要
- 推定したスキーマに対して、独立した 3 つのワークロードクエリを実行し、ベースラインを確立します。
- より適切なカラム型を持つテーブルを作成して同じデータを読み込み、クエリを再実行します。
- 同じ最適化済みスキーマと順序キーを持つ別のテーブルを作成し、再度クエリを実行します。
ベースラインとなるワークロードを定義する
これらの設定は、テスト中の繰り返し実行結果を比較可能にするのに役立ちます。測定が完了したら、元の値に戻してください。
system.query_log からこれらの値を取得する方法を含む測定ワークフローの全体については、再現可能なベースラインを確立するを参照してください。
計算した乗車速度でフィルタリングする
日付範囲内の乗車記録を集計する
乗客数でフィルタリング
3 つのクエリはいずれも約 3 億 2,900 万行を読み取っており、これはテーブルの行数にほぼ等しい値です。このことから、ワークロードの 2 つの側面を改善できる可能性が見えてきます。まず、選択したカラムの処理コストを下げ、次にフィルターで絞り込める場合は選択する行数を減らします。
スキーマを最適化する
不要な Nullable カラムを避ける
Nullable カラムでは、値に加えて null マスクも保存されます。null 値と型のデフォルト値を区別する必要がある場合は Nullable を使用しますが、値が必ず存在するカラムでは使用を避けてください。
例のスキーマで使用しているカラムの null 値をカウントします。
ratecode_id、mta_tax、payment_type だけです。最適化後のスキーマでは、これらのカラムには Nullable を残し、他のカラムからは削除します。
繰り返し値には LowCardinality を使用する
LowCardinality は辞書エンコーディングを使用し、繰り返し値の多いカラムのストレージ使用量と処理負荷を削減できます。適用する前に、異なる値の数を確認してください。
LowCardinality の有力な候補ですが、ワークロードに対する効果は実際に測定する必要があります。候補を特定する際の目安としては、異なる値が約10,000個であることが有用ですが、これは固定の上限ではありません。
より適切なデータ型を選択する
Int64 や Float64 を置き換える前に、数値カラムの最小値と最大値を確認します。
UInt8 に収まりますが、passenger_count は最大値である 255 に達します。この例では、trip_distance に Float32、金額には Decimal32 も使用しています。このデータセット内のすべての値は対象の範囲内に収まり、この例では集計結果を比較するワークロードであるため、浮動小数点精度の低下とセント単位の金額精度を許容しています。ソース値の完全な精度が必要な場合は、より広い範囲のソース型を維持してください。この例のクエリでは秒未満の精度が不要なため、同じ UTC タイムゾーン内で、推論された DateTime64 カラムを DateTime に置き換えています。
これらの選択は、このデータセットに固有のものです。同じ変更を適用する前に、本番データに求められる範囲、精度、NULL 許容性を確認してください。
スキーマの変更を適用する
nyc_taxi.trips_small_inferred を nyc_taxi.trips_small_no_pk に置き換え、3 つのクエリをすべて再実行します。元の例では、次のような代表値が得られました。
クエリが読み取る行数は同じですが、最適化されたスキーマでは、それらの行が表すデータ量が削減されます。そのため、選択するデータを変えずに、クエリの実行時間とピークメモリを改善できます。
2 つのテーブルのディスク上のサイズを比較します。
順序キーを最適化する
MergeTree ファミリーでは、順序キーによって行のディスク上での配置が決まります。ClickHouse はこの順序に基づいてスパースプライマリインデックスを構築し、クエリのフィルター条件を満たさないグラニュールをスキップします。多くのトランザクション型データベースの主キーとは異なり、一意性を保証するものではありません。
順序キーには、重要で繰り返し実行されるクエリで使用するフィルターを反映させる必要があります。カラムの順序は重要です。クエリが有用なプレフィックスでフィルタリングする場合、キーは最も効果的に機能します。カーディナリティの低いカラムは、頻繁にフィルタリングされる場合、効果的な先頭のキー要素となることがあります。また、時間ベースのワークロードでは、時間の部分が役立つことがよくあります。選択に関する詳しいガイダンスについては、主キーの選択を参照してください。
この例では、(passenger_count, pickup_datetime, dropoff_datetime) を使用します。passenger_count は異なる値が少なく、乗客数でフィルタリングする条件に使用されます。一方、pickup_datetime は日付範囲の集約に使用されます。pickup_datetime は先頭のカラムではありませんが、先頭カラムに条件が指定されていない場合でも、ClickHouse は後続のキーカラムの値を使用してデータを除外できます。一般に、順序キーの有用なプレフィックスでフィルタリングすると、より効果的に枝刈りできます。
順序キーの変更を適用する
nyc_taxi.trips_small_pk に置き換えた後、3つのクエリをすべて再実行します。
結果を比較する
スキーマを最適化するとストレージ使用量が削減され、選択した値をより低コストで処理できます。日付範囲集計では、ClickHouse が指定した日付範囲外のグラニュールをスキップできるため、順序キーによる追加の改善が最も大きくなります。乗客数フィルターでも、最初のキーカラムを条件にフィルタリングするため、読み取り行数が減少します。計算速度フィルターでは、順序キーの有効なプレフィックスではなく、
pickup_datetime、dropoff_datetime、trip_distance からフィルター条件が導出されるため、引き続きテーブル全体を読み取ります。
EXPLAIN indexes = 1 を使用して日付範囲集計を確認します。
ClickHouse 25.9 以降では、これらの設定により、
EXPLAIN で使用された索引と、それらによって除外されたパーツおよびグラニュールが報告されます。ワークロードにこの手法を適用する
- ベースラインの実行時間、読み取り行数とバイト数、ピークメモリを記録します。
- 選択したカラムに、不必要に幅の広い型や許容範囲の広い型が使われていないか確認します。
- データレイアウトを変更せずに、スキーマ変更を適用して測定します。
- 重要で繰り返し実行されるクエリで使用されるフィルターに基づいて、順序キーをテストします。
EXPLAIN indexes = 1で選択されるデータを比較し、同等の条件でベースラインクエリを再実行します。