本トピックでは、Hologres の主要なインデックス(分散キー、イベント時刻列(セグメントキー)、クラスタリングキー)について説明し、Hologres 開発時のクエリパフォーマンス向上を支援します。
Hologres 分散データウェアハウスの基本原理
Hologres は分散型データウェアハウスです。並列計算およびベクター計算を活用して、数秒以内のクエリ応答を実現します。そのため、データ分布の特性はパフォーマンスにとって極めて重要です。これらの特性には、複数の分散ノードにわたる均等なデータ分布(distribution_key)と、単一ノード内のファイルにおけるデータの順序付き分布(event_time_column/segment_key)が含まれます。Hologres は、オンライン分析処理(OLAP)シナリオ向けにデフォルトでカラムストア形式を採用しています。このため、ファイル内におけるデータの順序付き分布(clustering_key)も非常に重要となります。これらの 3 つの概念を理解することで、パフォーマンスを大幅に向上させることができます。データ分布の特性はデータ書き込み時に決定され、後から調整するにはコストがかかります。したがって、テーブル作成時にデータレイアウトに関連するこれら 3 つのプロパティを設計する必要があります。データレイアウトとは直接関係しないプロパティ(ビットマップ索引(bitmap_columns)や辞書エンコーディング(dictionary_columns)など)は、テーブル作成後に必要に応じて調整可能です。
Hologres はまた、データベース > スキーマ > テーブルの 3 段階メタデータ構造を採用しています。論理的に関連するテーブルは同一スキーマ内に配置し、クロスデータベースクエリを回避してください。データベースは、リソース隔離ではなく、メタデータ隔離の基本単位です。
SQL 最適化の基本原則:I/O の削減と同時実行数の最適化
テーブル作成時に適切なデータ分布を設計することで、SQL 実行時にデータを迅速に特定できます。これにより I/O 消費が削減され、少ないコンピューティングリソースでより高いクエリパフォーマンスを実現できます。また、均等なデータ分布は、同時実行リソースを十分に活用し、単一ポイントのボトルネックを回避します。以下の図は、SQL クエリの実行フロー(開始からデータ取得まで)を示しています。この図を参照して、I/O を削減する方法をご確認ください。
パーティションプルーニング:パーティションテーブルに対する SQL クエリ実行時には、必要なパーティションを特定するためにパーティションプルーニングが使用されます。クエリにパーティションキーに対するフィルター条件が含まれていない場合、すべてのパーティションを走査する必要があります。これにより過剰な I/O スキャンが発生します。通常、パーティションの粒度として「日単位」が適切です。非パーティション化テーブルでは、このステップはスキップされます。
シャードプルーニング:分散キー(distribution_key)を使用して、データを含むデータシャードを迅速に特定します。これにより、単一 SQL クエリのリソース消費を削減し、同時実行 SQL クエリのスループットを向上させます。特定のシャードを特定できない場合、分散フレームワークはすべてのシャードを計算に参加させます。これにより、単一 SQL クエリの並列処理の次数が増加し、より多くのリソースが使用されるため、全体の同時実行数が低下します。また、集中実行を必要とする一部の演算子は、追加のシャッフリングオーバーヘッドを引き起こします。通常、注文 ID、ユーザー ID、イベント ID のような均等な分布を持つフィールドを分散キーとして選択することを推奨します。結合が必要な複数のテーブルで同一の分散キーを設定すると、関連するデータが同一シャードに分散されます。これにより、ローカル JOIN を実行でき、JOIN 効率が向上します。
セグメントキー プルーニング:セグメントキー(event_time_column/segment_key)を使用して、単一ノード上の複数のファイルからデータを含むファイルを迅速に特定します。これにより、不要なファイルのオープンを回避できます。フィルタリングが不可能な場合、ノード上のすべてのファイルを走査する必要があります。
クラスタリングキー プルーニング:クラスタリングキー(clustering_key)を使用して、単一ファイル内のデータセグメントを迅速に特定します。これにより、範囲クエリおよびフィールドソートの効率が向上します。
SQL 最適化の実践例
本セクションでは、TPC-H クエリを例として、Hologres インデックスを設定してクエリパフォーマンスを向上させる方法を説明します。TPC-H の詳細については、「テスト計画の概要」をご参照ください。
TPC-H SQL の参考実践例
TPC-H Q1 クエリ
TPC-H Q1 は、lineitem テーブルの特定の列に対して集計およびフィルタリングを行います。
l_shipdate <=:クエリのフィルタリング条件です。範囲フィルタリングをサポートし、データフィルタリングを高速化するために、インデックスを設定する必要があります。
--TPC-H Q1
SELECT
l_returnflag,
l_linestatus,
SUM(l_quantity) AS sum_qty,
SUM(l_extendedprice) AS sum_base_price,
SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
AVG(l_quantity) AS avg_qty,
AVG(l_extendedprice) AS avg_price,
AVG(l_discount) AS avg_disc,
COUNT(*) AS count_order
FROM
lineitem
WHERE
l_shipdate <= DATE '1998-12-01' - INTERVAL '120' DAY
GROUP BY
l_returnflag,
l_linestatus
ORDER BY
l_returnflag,
l_linestatus;TPC-H Q4 クエリ
TPC-H Q4 は、主に lineitem テーブルと orders テーブルの結合クエリを実行します。
o_orderdate >= DATE '1996-07-01':フィルタリング条件です。範囲フィルタリングをサポートし、必要なデータを迅速に特定するために、インデックスを設定できます。l_orderkey = o_orderkey:2 つのテーブル間の結合条件です。両テーブルで同一のインデックスを設定できます。2 つのテーブル間のインタラクション時にデータシャッフリング操作を削減するため、ローカル JOIN を実行することを推奨します。--TPC-H Q4 Query SELECT o_orderpriority, COUNT(*) AS order_count FROM orders WHERE o_orderdate >= DATE '1996-07-01' AND o_orderdate < DATE '1996-07-01' + INTERVAL '3' MONTH AND EXISTS ( SELECT * FROM lineitem WHERE l_orderkey = o_orderkey AND l_commitdate < l_receiptdate ) GROUP BY o_orderpriority ORDER BY o_orderpriority;
テーブル作成に関する推奨事項
Q1 および Q4 クエリでは、lineitem テーブルおよび orders テーブルが使用されます。
hologres_dataset_tpch_100g.lineitem
Q1 および Q4 の両クエリで lineitem テーブルが使用されますが、使用されるフィールドおよびクエリ条件は異なります。
Q1 クエリの場合:クエリでは主に l_shipdate を使用して範囲フィルタリングを行います。クラスタリングキー(clustering_key)は、ファイル内のデータの順序を利用して範囲フィルタリングを高速化します。したがって、l_shipdate をクラスタリングキーとして設定できます。 セグメントキー(event_time_column)は、ファイル間の順序を維持するために使用されます。単調増加または単調減少する日付フィールドについては、セグメントキーとしても設定することを推奨します。これにより、ファイルレベルでの効果的なフィルタリングが可能になります。したがって、l_shipdate をセグメントキーとしても設定できます。
Q4 クエリの場合:クエリでは、lineitem テーブルの l_orderkey フィールドを用いて、orders テーブルの o_orderkey フィールドと結合します。分散キー(distribution_key)は、データ分布ポリシーを指定します。システムは、同一の distribution_key 値を持つデータを同一シャード上に格納します。2 つのテーブルが同一テーブルグループに属し、その結合フィールドが分散キーである場合、データ書き込み時に、両テーブルの同一キー値を持つレコードが自動的に同一シャードに分散されます。これらのテーブルを結合する際には、現在のノード上でローカル JOIN が実行されます。これにより、実行時に結合キーに基づくデータシャッフリングおよび再配布を回避でき、実行効率を大幅に向上させることができます。したがって、l_orderkey を分散キーとして設定してください。
lineitem テーブルの最終的なテーブルスキーマは以下のとおりです:
BEGIN; CREATE TABLE hologres_dataset_tpch_100g.lineitem ( l_ORDERKEY BIGINT NOT NULL, L_PARTKEY INT NOT NULL, L_SUPPKEY INT NOT NULL, L_LINENUMBER INT NOT NULL, L_QUANTITY DECIMAL(15,2) NOT NULL, L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL, L_DISCOUNT DECIMAL(15,2) NOT NULL, L_TAX DECIMAL(15,2) NOT NULL, L_RETURNFLAG TEXT NOT NULL, L_LINESTATUS TEXT NOT NULL, L_SHIPDATE TIMESTAMPTZ NOT NULL, L_COMMITDATE TIMESTAMPTZ NOT NULL, L_RECEIPTDATE TIMESTAMPTZ NOT NULL, L_SHIPINSTRUCT TEXT NOT NULL, L_SHIPMODE TEXT NOT NULL, L_COMMENT TEXT NOT NULL, PRIMARY KEY (L_ORDERKEY,L_LINENUMBER) ) WITH ( distribution_key = 'L_ORDERKEY',--テーブル結合時にローカル JOIN を有効化します。 clustering_key = 'L_SHIPDATE',--範囲フィルタリングを高速化します。 event_time_column = 'L_SHIPDATE'--ファイルプルーニングを高速化します。 ); COMMIT;
hologres_dataset_tpch_100g.orders
本例では、Q4 クエリで orders テーブルが使用されます。
ローカル JOIN 機能を活用し、結合クエリの効率を向上させるために、orders テーブルの o_orderkey フィールドを分散キーとして設定してください。
o_orderdate フィールドは、主に日付フィールドに対するフィルタリングクエリで使用されます。ファイルプルーニングを高速化するために、セグメントキーとして設定できます。
orders テーブルの最終的なテーブルスキーマは以下のとおりです:
BEGIN; CREATE TABLE hologres_dataset_tpch_100g.orders ( O_ORDERKEY BIGINT NOT NULL PRIMARY KEY, O_CUSTKEY INT NOT NULL, O_ORDERSTATUS TEXT NOT NULL, O_TOTALPRICE DECIMAL(15,2) NOT NULL, O_ORDERDATE timestamptz NOT NULL, O_ORDERPRIORITY TEXT NOT NULL, O_CLERK TEXT NOT NULL, O_SHIPPRIORITY INT NOT NULL, O_COMMENT TEXT NOT NULL ) WITH ( distribution_key = 'O_ORDERKEY',--テーブル結合時にローカル JOIN を有効化します。 event_time_column = 'O_ORDERDATE'--ファイルプルーニングを高速化します。 ); COMMIT;
サンプルデータのインポート
HoloWeb のワンクリックパブリックデータセットインポート機能を使用して、TPC-H 100 GB データセットを Hologres インスタンスに迅速にインポートできます。詳細については、「パブリックデータセットのインポート」をご参照ください。
パフォーマンステスト結果の比較
テーブルに適切なプロパティ(インデックス)を設定した後、最適化前後のパフォーマンスをテストできます。
テスト環境
インスタンスタイプ:32 コア。
ネットワークタイプ:VPC。
PSQL クライアントを使用してクエリを 2 回実行し、2 回目の実行時間を記録します。
テストの結論
単一テーブルに対するフィルタリングクエリでは、フィルター対象のフィールドをクラスタリングキーとして設定することで、クエリを大幅に高速化できます。
複数テーブルの結合クエリでは、結合対象のフィールドを分散キーとして設定することで、結合効率を大幅に向上させることができます。
クエリ
Hologres インデックスありの遅延
Hologres インデックスなしの遅延
Q1
48.293 ms
59.483 ms
Q4
822.389 ms
3027.957 ms
付録
さらに詳しく
技術原理
Hologres のアーキテクチャ、ストレージエンジン、コンピュートエンジンなどのコア原理について学ぶには、「Alibaba Cloud のクラウドネイティブリアルタイムデータウェアハウスのコア技術」をご参照ください。
サービスの有効化
インスタンスタイプの選択方法については、「インスタンス管理」をご参照ください。
RAM ユーザーの権限付与については、「RAM ユーザーの権限付与のクイックスタート」をご参照ください。
データインポート
Flink を使用したリアルタイム書き込みおよびディメンションテーブルクエリについては、「Flink 完全管理」をご参照ください。
MySQL、Oracle、PolarDB などのデータベース全体のリアルタイム同期については、「データソースの設定(MySQL)」をご参照ください。
OSS データのインポートについては、「DLF を基盤とした OSS 上のデータレイクへのアクセス高速化」をご参照ください。
データ書き込みおよび更新の効率を大幅に向上させるには、Fixed Plan を使用してください。詳細については、「Fixed Plan を使用した SQL 実行の高速化」をご参照ください。
データクエリ
シナリオ別テーブル作成の推奨事項については、「シナリオ別テーブル作成およびチューニングガイド」をご参照ください。
テーブル作成時には、distribution_key、clustering_key、event_time_column、bitmap_index などの主要パラメーターを理解し、合理的なテーブルスキーマを設定することで、パフォーマンスを大幅に向上させることができます。詳細については、「CREATE TABLE」をご参照ください。
内部テーブルのパフォーマンスチューニングについては、「クエリパフォーマンスの最適化」をご参照ください。
MaxCompute データに対するクエリの高速化については、「外部テーブルを基盤とした MaxCompute データのクエリ高速化」をご参照ください。
サービスモニタリング
アクティブなクエリのトラブルシューティングおよびロックの確認については、「クエリ管理」をご参照ください。
失敗または長時間実行中のクエリのトラブルシューティングについては、「スロークエリログの表示および分析」をご参照ください。
読み書き分離および負荷隔離については、「読み書き分離のためのプライマリインスタンスとセカンダリインスタンスの展開(共有ストレージ)」をご参照ください。
ベストプラクティスおよびユースケース
ベストプラクティスおよびユースケースのコレクションについては、「典型的な業界シナリオおよび代表的な顧客ユースケースのベストプラクティス」をご参照ください。