|
Apache Iceberg は、タイムトラベル、ACID トランザクション、パーティション進化、スキーマ進化などの強力な機能とオープン エコシステムを提供するオープン ソースの Lakehouse テーブル形式です。 この記事では、インデックスを導入することでクエリ パフォーマンスを向上させる、Iceberg コンポーネントに対する Volcano Engine EMR チームの最適化戦略について説明します。 Iceberg を使用してデータ レイク ウェアハウスを構築するVolcano Engine E-MapReduce(EMR)は、Volcano Engine Digital Intelligence Platform(VeDI)を基盤とするクラウドネイティブのオープンソース・ビッグデータ・プラットフォーム製品です。Hadoop、Spark、Flink、Hive、Presto、Kafka、StarRocks、Doris、Hudi、Icebergといったエンタープライズグレードのビッグデータ・エコシステム・コンポーネントを提供し、100%のオープンソース互換性を実現しています。企業がエンタープライズグレードのビッグデータ・プラットフォームを迅速に構築し、運用のハードルを下げるのに役立ちます。業界をリードするEMRステートレス・コンセプトに準拠したVolcano Engine EMRは、クラスターレベルの柔軟なスケーリングを可能にします。ビジネス需要がないときにはクラスターを解放し、需要があるときには再起動します。インテリジェントなホットデータとコールドデータの階層型ストレージ機能と組み合わせることで、企業はビッグデータ・インフラストラクチャ分野におけるコストをさらに削減し、効率を向上させることができます。 Volcano Engine EMR製品をベースとして、データレイク・ウェアハウス、準リアルタイム・データウェアハウス、そしてリアルタイム・データウェアハウスを構築できます。例えば、Icebergを用いてデータレイク・ウェアハウスを構築し、ODSからDWDまでの様々なレイヤーをモデリングし、HFDSまたはTOS(Volcano Engineオブジェクトストレージ製品)にデータを保存し、TrinoまたはSparkを用いて分析することが可能です。 クエリ パフォーマンスを高速化して、専用の分散データ ウェアハウス (ClickHouse など) のパフォーマンスにできるだけ近づける方法は、検討して調査する必要がある問題です。 インデックスはクエリパフォーマンスを向上させるための業界で一般的な手法であり、Icebergでもこのアプローチを採用しました。頻繁に使用される列にインデックスを構築し、テーブルスキャン時にこれらのインデックスが一致するデータのみを返すようにすることで、照合対象となるデータの量を削減し、クエリパフォーマンスを大幅に向上させました。 氷山の紹介Iceberg Indexの機能を紹介する前に、Icebergアーキテクチャについて簡単に説明します。Icebergは、以下に示すように階層化されたメタデータアーキテクチャを採用しています。 Spark、Presto、Flinkなどの複数のエンジンは、階層的なメタデータを使用してデータファイルのリストを検索し、Icebergからデータを読み取ります。例えば、SparkエンジンはSQL文を解析し、IcebergのAPIを呼び出してデータファイルを取得し、タスクに分割します。 マニフェスト ファイルには、データ ファイル内のフィールドの最大値と最小値が記録されます。 この情報を使用すると、データ ファイル レベルで予備フィルタリングを実行し、基準を満たさないデータ ファイルを除外して、読み取られるデータの量を削減できます。 インデックス実装の必要性Iceberg はすでにデータ ファイル レベルのフィルタリングを提供しているのに、なぜインデックスを導入する必要があるのでしょうか。次の例でこれを示します。左側の 2 つのテーブルはデータ ファイルの内容を表し、右側のテーブルは対応するマニフェスト ファイルを表しています。 しかし、 1. まず、インデックスの種類を調べます。 インデックスの種類には、ブルームフィルタ、リボンフィルタ、ディクショナリインデックス、ビットマップなど、様々なものがあります。多次元分析シナリオのニーズを満たすため、高カーディナリティシナリオに適しており、=、<、>、IN、BETWEENなどの演算を用いた多次元分析をサポートする[Range-Encoded BitMap](https://www.featurebase.com/blog/range-encoded-bitmaps)(Base-2、ビットスライスインデックス)を選択しました。 例えば、上記の「name」列と「age」列のインデックス情報を計算してみましょう。「name」は文字列型であるため、インデックス情報を計算する前に辞書エンコードする必要があります。Range-Encoded技術を使用することで、データのバイナリ情報と対応する「pos」情報に基づいてインデックスデータが生成されます。インデックスデータを分析すると つまり、BitMap の交差および結合操作により、複雑なフィルタリング条件下でもより多くのデータ ファイルをフィルタリングできるようになります。 2. 次に、インデックスの粒度を調べます。 IcebergのMin-Maxもファイルレベルインデックスの一種です。ファイルレベルインデックスは、フィルター条件を満たさないデータファイルを除外します。ファイルレベルインデックスは様々なファイルタイプに適用できますが、粒度は比較的粗いです。データファイル内のレコードが1つでも条件を満たすと、そのファイル内のすべてのデータが読み込まれ、計算に使用されるため、SQLクエリのパフォーマンスに影響します。 ParquetおよびORCファイル形式では、ファイルチャンク(行グループまたはストライプ)の概念が提供されており、行グループ/ストライプの粒度でデータをフィルタリングできます。(説明を簡単にするために、行グループとストライプの両方をまとめて「スプリット」と呼びます。) 例えば、SQL文「 SQL 文を解析した後、条件を満たすデータファイルのリストが分割され、多数の分割リストが生成されます。インデックスを使用して、各分割内のデータが条件を満たしているかどうかが分析され、満たしていない場合はスキップされます。上図に示すように、データファイルリストを分割した後、数万の分割が得られます。split1 にインデックスデータを適用すると、split1 には条件 したがって、行グループ/ストライプ レベルできめ細かいインデックスを使用すると、ほとんどのデータをフィルター処理できます。 きめ細かなインデックス実装ロジックIceberg メタデータのマニフェスト ファイルは、最小値と最大値などの統計情報の提供に加えて、分割関連の情報も提供します:
Icebergはインデックス構築用のAPIを提供しており、エンジンはこのAPIを呼び出すことでインデックス構築機能を実装できます。Spark 3.3以降では、インデックス付きSQL文が既に提供されています。IcebergのSparkモジュールで、Sparkが提供するインデックスインターフェースを実装するだけで済みます。
メインタスクに影響を与えない非同期インデックス構築を採用しています。また、追加されたデータのみをインデックス化する増分インデックス構築機能も提供しています。TableScanを呼び出してデータを読み取り、データファイルの分割オフセットに従ってデータを分割し、インデックスを構築し、インデックスデータと対応するメタデータを保存します。ファイルサイズが小さくなるのを避けるため、インデックスデータはマージされます。
インデックスファイルはバイナリ形式の[puffin]https://iceberg.apache.org/puffin-spec/形式を使用します。 フッターには各BLOBのメタデータ情報が保存されます。インデックスが正常に構築されると、次のようなファイルが生成されます。 インデックスの利点範囲エンコードされたビットマップは多次元分析シナリオに適しており、その有効性は範囲が狭い場合に特に顕著です。以下では、Sparkエンジンに基づくパフォーマンステストを紹介します。
左のグラフは、インデックスありとなしの7つのSQL文の実行時間を示しています。右のグラフは、インデックス使用後の7つのSQL文のデータ分割回数を示しています。データ分割回数が少ないほど、インデックスのパフォーマンスが向上することは明らかです。最悪のケースでは、すべての分割がパラメータを用いて計算され、インデックスを構築しない場合と同等の効果が得られます。
SSBが提供するテストシナリオは、Range-Encodedの有利なシナリオと完全には一致しないため、インデックス使用の効果は顕著ではありません。ただし、インデックスを使用しない場合よりも悪くなることはありません。下の左の図に示すように、SQL文の実行時間はインデックス構築前後であり、インデックス構築のメリットは明らかではありません。右の図では、すべての分割が計算に関係していることがわかります。 要約上記の紹介に基づいて、Iceberg のインデックス実装のいくつかの特徴をまとめると次のようになります。
|