ビットマップインデックス
このトピックでは、ビットマップインデックスの作成と管理方法、および使用例について説明します。
はじめに
ビットマップインデックスは、ビットマップ(ビットの配列)を使用する特別なデータベースインデックスです。ビットは常に2つの値のいずれか(0または1)を持ちます。ビットマップの各ビットはテーブル内の1行に対応し、各ビットの 値は対応する行の値に依存します。
ビットマップインデックスは、特定の列のクエリパフォーマンスを向上させるのに役立ちます。クエリのフィルタ条件がプレフィックスインデックスに一致する場合、クエリ効率を大幅に向上させ、迅速に結果を返すことができます。ただし、テーブルには1つのプレフィックスインデックスしか持てません。クエリのフィルタ条件がプレフィックスインデックスのプレフィックスを含まない場合、その列にビットマップインデックスを作成してクエリ効率を向上させることができます。
クエリを高速化するためのビットマップインデックスの設計方法
ビットマップインデックスを選択する際の主な考慮事項は、列の基数とクエリに対するビットマップインデックスのフィルタリング効果です。一般的な誤解とは異なり、StarRocksのビットマップインデックスは、高基数の列に対するクエリや、複数の低基数列の組み合わせに対するクエリにより適しています。さらに、ビットマップインデックスはデータを効果的にフィルタリングし、少なくとも999/1000のデータをフィルタリングすることで、読み込むページデータの量を減らすことができます。
単一の低基数列に対するクエリでは、ビットマップインデックスのフィルタリング効果は低く、多くの行を読み込む必要があり、複数のページに分散されているためです。
ビットマップインデックスがクエリに与えるフィルタリング効果を評価する際には、データロードのコストを考慮する必要があります。StarRocksでは、基礎データはページ(デフォルトサイズは64K)で整理され、ロードされます。データロードのコストには、ディスクからページをロードする時間、ページを解凍する時間、デコードする時間が含まれます。
ただし、過度に高い基数は、より多くのディスクスペースを占有するなどの問題を引き起こす可能性があります。また、ビットマップインデックスはデータロード中に構築される必要があるため、頻繁なデータロードはロードパフォーマンスに影響を与える可能性があります。
さらに、クエリ中のビットマップインデックスのロードオーバーヘッドも考慮する必要があります。クエリ中にビットマップインデックスはオンデマンドでロードされ、クエリ条件に関与する列の値の数/基数 x ビットマップインデックス
の値が大きいほど、クエリ中のビットマップインデックスのロードオーバーヘッドが 大きくなります。
ビットマップインデックスの適切な基数とクエリ条件を決定するために、このトピックのビットマップインデックスのパフォーマンステストを参照してパフォーマンステストを実施することをお勧めします。実際のビジネスデータとクエリを使用して、異なる基数の列にビットマップインデックスを作成し、クエリに対するビットマップインデックスのフィルタリング効果(少なくとも999/1000のデータをフィルタリング)、ディスクスペースの使用量、ロードパフォーマンスへの影響、クエリ中のビットマップインデックスのロードオーバーヘッドを分析することができます。
StarRocksには、ビットマップインデックスの適応選択メカニズムが組み込まれています。ビットマップインデックスがクエリを高速化できない場合、例えば、多くのページをフィルタリングできない場合や、クエリ中のビットマップインデックスのロードオーバーヘッドが高い場合、クエリ中に使用されないため、クエリパフォーマンスに大きな影響を与えることはありません。
ビットマップインデックスの適応選択
StarRocksは、列の基数とクエリ条件に基づいてビットマップインデックスを使用するかどうかを適応的に選択できます。ビットマップイ ンデックスが多くのページを効果的にフィルタリングできない場合や、クエリ中のビットマップインデックスのロードオーバーヘッドが高い場合、StarRocksはデフォルトでビットマップインデックスを使用しないため、クエリパフォーマンスの低下を避けます。
StarRocksは、クエリ条件に関与する値の数と列の基数の比率に基づいてビットマップインデックスを使用するかどうかを判断します。一般に、この比率が小さいほど、ビットマップインデックスのフィルタリング効果が良好です。したがって、StarRocksはbitmap_max_filter_ratio/1000
をしきい値として使用します。フィルタ条件の値の数/列の基数
がbitmap_max_filter_ratio/1000
より小さい場合、ビットマップインデックスが使用されます。bitmap_max_filter_ratio
のデフォルト値は1
です。
単一の列に基づくクエリの例として、SELECT * FROM employees WHERE gender = 'male';
を考えてみましょう。employees
テーブルのgender
列には、'male'と'female'の値があるため、基数は2(2つの異なる値)です。クエリ条件には1つの値が含まれているため、比率は1/2であり、1/1000より大きいです。したがって、このクエリではビットマップインデックスは使用されません。
複数の列の組み合わせに基づくクエリの例として、SELECT * FROM employees WHERE gender = 'male' AND city IN ('Beijing', 'Shanghai');
を考えてみましょう。city
列の基数は10,000であり、クエリ条件には2つの値が含まれているため、比率は(1*2)/(2*10000)
であり、1/1000より小さいです。したがって、このクエリではビットマップインデックスが使用されます。
bitmap_max_filter_ratio
の値の範囲は1-1000です。bitmap_max_filter_ratio
が1000
に設定されている場合、ビットマップインデックスを持つ列に対するクエリは強制的にビットマップインデックスを使用します。
利点
- ビットマップインデックスは、クエリされた列の値の行番号を迅速に特定でき、ポイントクエリや小範囲のクエリに適しています。
- ビットマップインデックスは、ユニオンやインターセクション操作(ORおよびAND操作)を含む多次元クエリを最適化できます。
考慮事項
最適化可能なクエリ
ビットマップインデックスは、等価=
クエリ、[NOT] IN
範囲クエ リ、>
, >=
, <
, <=
クエリ、およびIS NULL
クエリを最適化するのに適しています。!=
および[NOT] LIKE
クエリの最適化には適していません。
サポートされる列とデータ型
ビットマップインデックスは、プライマリキーおよび重複キーテーブルのすべての列、および集約テーブルとユニークキーテーブルのキー列に作成できます。ビットマップインデックスは、次のデータ型の列に作成できます。
- 日付型: DATE, DATETIME.
- 数値型: TINYINT, SMALLINT, INT, BIGINT, LARGEINT, DECIMAL, BOOLEAN.
- 文字列型: CHAR, STRING, VARCHAR.
- その他の型: HLL.
基本操作
インデックスの作成
-
テーブル作成時にビットマップインデックスを作成します。
CREATE TABLE `lineorder_partial` (
`lo_orderkey` int(11) NOT NULL COMMENT "",
`lo_orderdate` int(11) NOT NULL COMMENT "",
`lo_orderpriority` varchar(16) NOT NULL COMMENT "",
`lo_quantity` int(11) NOT NULL COMMENT "",
`lo_revenue` int(11) NOT NULL COMMENT "",
INDEX lo_orderdate_index (lo_orderdate) USING BITMAP
) ENGINE=OLAP
DUPLICATE KEY(`lo_orderkey`)
DISTRIBUTED BY HASH(`lo_orderkey`) BUCKETS 1;この例では、
lo_orderdate
列にlo_orderdate_index
という名前のビットマップインデックスが作成されています。ビットマップインデックスの命名要件については、システム制限を参照してください。同一のビットマップインデックスは同じテーブル内に作成できません。複数の列に対して複数のビットマップインデックスを作成することができ、カンマ(,)で区切ります。
注記テーブル作成の詳 細なパラメータについては、CREATE TABLEを参照してください。
-
CREATE INDEX
を使用して、テーブル作成後にビットマップインデックスを作成することができます。詳細なパラメータの説明と例については、CREATE INDEXを参照してください。CREATE INDEX lo_quantity_index ON lineorder_partial (lo_quantity) USING BITMAP;
インデックス作成の進捗
ビットマップインデックスの作成は非同期プロセスです。インデックス作成ステートメントを実行した後、SHOW ALTER TABLEコマンドを使用してインデックス作成の進捗を確認できます。返された値のState
フィールドがFINISHED
を示している場合、インデックスは正常に作成されています。
SHOW ALTER TABLE COLUMN;
各テーブルには、同時に1つのスキーマ変更タスクしか存在できません。現在のビットマップインデックスが作成されるまで、新しいビットマップインデックスを作成することはできません。
インデックスの表示
指定されたテーブルのすべてのビットマップインデックスを表示します。詳細なパラメータと返される結果については、SHOW INDEXを参照してください。
SHOW INDEXES FROM lineorder_partial;
ビットマップインデックスの作成は非同期プロセスです。上記のステートメントを使用して、正常に作成されたインデックスのみを表示できます。
インデックスの削除
指定されたテーブルのビットマップインデックスを削除します。詳細なパラメータと例については、DROP INDEXを参照してください。
DROP INDEX lo_orderdate_index ON lineorder_partial;
ビットマップインデックスがクエリを高速化するかどうかを確認する
クエリプロファイルのBitmapIndexFilterRows
フィールドを確認します。プロファイルの表示方法については、クエリ分析を参照してください。
ビットマップインデックスのパフォーマンステスト
テストの目的
異なる基数のクエリに対するビットマップインデックスのフィルタリング効果やその他の影響(ディスク使用量など)を分析します。
このセクションでは、ビットマップインデックスを常に使用する場合と、ビットマップインデックスを適応的に使用する場合のパフォーマンスを比較し、StarRocksのビットマップインデックスの適応選択の有効性を検証します。
テーブルとビットマップインデックスの作成
ページデータのキャッシュがクエリパフォーマンスに影響を与えないようにするために、BEの設定項目disable_storage
がtrue
に設定されていることを確認してください。
このセクションでは、テーブルlineorder
(SSB 20G)を例にとります。
-
参照用のオリジナルテーブル(ビットマップインデックスなし)
CREATE TABLE `lineorder_without_index` (
`lo_orderkey` int(11) NOT NULL COMMENT "",
`lo_linenumber` int(11) NOT NULL COMMENT "",
`lo_custkey` int(11) NOT NULL COMMENT "",
`lo_partkey` int(11) NOT NULL COMMENT "",
`lo_suppkey` int(11) NOT NULL COMMENT "",
`lo_orderdate` int(11) NOT NULL COMMENT "",
`lo_orderpriority` varchar(16) NOT NULL COMMENT "",
`lo_shippriority` int(11) NOT NULL COMMENT "",
`lo_quantity` int(11) NOT NULL COMMENT "",
`lo_extendedprice` int(11) NOT NULL COMMENT "",
`lo_ordtotalprice` int(11) NOT NULL COMMENT "",
`lo_discount` int(11) NOT NULL COMMENT "",
`lo_revenue` int(11) NOT NULL COMMENT "",
`lo_supplycost` int(11) NOT NULL COMMENT "",
`lo_tax` int(11) NOT NULL COMMENT "",
`lo_commitdate` int(11) NOT NULL COMMENT "",
`lo_shipmode` varchar(11) NOT NULL COMMENT ""
) ENGINE=OLAP
DUPLICATE KEY(`lo_orderkey`)
DISTRIBUTED BY HASH(`lo_orderkey`) BUCKETS 1; -
ビットマップインデックスを持つテーブル:
lo_shipmode
,lo_quantity
,lo_discount
,lo_orderdate
,lo_tax
,lo_partkey
に基づいてビットマップインデックスを作成CREATE TABLE `lineorder_with_index` (
`lo_orderkey` int(11) NOT NULL COMMENT "",
`lo_linenumber` int(11) NOT NULL COMMENT "",
`lo_custkey` int(11) NOT NULL COMMENT "",
`lo_partkey` int(11) NOT NULL COMMENT "",
`lo_suppkey` int(11) NOT NULL COMMENT "",
`lo_orderdate` int(11) NOT NULL COMMENT "",
`lo_orderpriority` varchar(16) NOT NULL COMMENT "",
`lo_shippriority` int(11) NOT NULL COMMENT "",
`lo_quantity` int(11) NOT NULL COMMENT "",
`lo_extendedprice` int(11) NOT NULL COMMENT "",
`lo_ordtotalprice` int(11) NOT NULL COMMENT "",
`lo_discount` int(11) NOT NULL COMMENT "",
`lo_revenue` int(11) NOT NULL COMMENT "",
`lo_supplycost` int(11) NOT NULL COMMENT "",
`lo_tax` int(11) NOT NULL COMMENT "",
`lo_commitdate` int(11) NOT NULL COMMENT "",
`lo_shipmode` varchar(11) NOT NULL COMMENT "",
INDEX i_shipmode (`lo_shipmode`) USING BITMAP,
INDEX i_quantity (`lo_quantity`) USING BITMAP,
INDEX i_discount (`lo_discount`) USING BITMAP,
INDEX i_orderdate (`lo_orderdate`) USING BITMAP,
INDEX i_tax (`lo_tax`) USING BITMAP,
INDEX i_partkey (`lo_partkey`) USING BITMAP
) ENGINE=OLAP
DUPLICATE KEY(`lo_orderkey`)
DISTRIBUTED BY HASH(`lo_orderkey`) BUCKETS 1;
ビットマップインデックスの ディスクスペース使用量
lo_shipmode
: 文字列型、基数7、130Mを占有lo_quantity
: 整数型、基数50、291Mを占有lo_discount
: 整数型、基数11、198Mを占有lo_orderdate
: 整数型、基数2406、191Mを占有lo_tax
: 整数型、基数9、160Mを占有lo_partkey
: 整数型、基数600,000、601Mを占有
クエリ1: 低基数の単一列に対するクエリ
ビットマップインデックスなしのテーブルに対するクエリ
クエリ:
SELECT count(1) FROM lineorder_without_index WHERE lo_shipmode="MAIL";
クエリパフォーマンス分析: クエリされたテーブルにはビットマップインデックスがないため、lo_shipmode
列のデータを含むすべてのページを読み込み、その後に述語フィルタリングが適用されます。
合計時間: 約0.91秒、データロードに0.47秒、低基数最適化のための辞書デコードに0.31秒、述語フ ィルタリングに0.23秒かかります。
PullRowNum: 20.566M (20566493) // 結果セットの行数。
CompressedBytesRead: 55.283 MB // 読み込まれたデータの総量。
RawRowsRead: 143.999M (143999468) // 読み込まれた行数。ビットマップインデックスがないため、この列のすべてのデータが読み込まれます。
ReadPagesNum: 8.795K (8795) // 読み込まれたページ数。ビットマップインデックスがないため、この列のデータを含むすべてのページが読み込まれます。
IOTaskExecTime: 914ms // データスキャンの合計時間。
BlockFetch: 469ms // データロードの時間。
DictDecode: 311.612ms // 低基数最適化のための辞書デコードの時間。
PredFilter: 23.182ms // 述語フィルタリングの時間。
PredFilterRows: 123.433M (123432975) // フィルタリングされた行数。
ビットマップインデックスを持つテーブルに対するクエリ
ビットマップインデックスを強制的に使用
ビットマップインデックスを強制的に使用するには、StarRocksの設定に従って、各BEノードのbe.confファイルでbitmap_max_filter_ratio=1000
を設定し、その後BEノードを再起動する必要があります。
クエリ:
SELECT count(1) FROM lineorder_with_index WHERE lo_shipmode="MAIL";
クエリパフォーマンス分析: クエリされた列が低基数であるため、ビットマップインデックスはデータを効率的にフィルタリングしません。ビットマップインデックスは実際のデータの行番号を迅速に特定できますが、多くの行を読み込む必要があり、複数のページに分散されている ため、読み込む必要のあるページを効果的にフィルタリングできません。さらに、ビットマップインデックスのロードとデータフィルタリングに追加のオーバーヘッドが発生し、合計時間が長くなります。
合計時間: 2.077秒、データとビットマップインデックスのロードに0.93秒、低基数最適化のための辞書デコードに0.33秒、ビットマップインデックスによるデータフィルタリングに0.42秒、ZoneMapインデックスによるデータフィルタリングに0.17秒かかります。
PullRowNum: 20.566M (20566493) // 結果セットの行数。
CompressedBytesRead: 72.472 MB // 読み込まれたデータの総量。この列のビットマップインデックスのサイズは130MBで、7つのユニークな値があります。各値のビットマップインデックスのサイズは18MBです。ページのデータサイズは55MB = 73MBです。
RawRowsRead: 20.566M (20566493) // 読み込まれた行数。実際に読み込まれた行数は2000万行です。
ReadPagesNum: 8.802K (8802) // 読み込まれたページ数。ビットマップインデックスによってフィルタリングされた2000万行が異なるページにランダムに分散されているため、すべてのページが読み込まれます。ページは最小のデータ読み込み単位です。したがって、ビットマップインデックスはページをフィルタリングしません。
IOTaskExecTime: 2s77ms // データスキャンの合計時間。ビットマップインデックスなしよりも長いです。
BlockFetch: 931.144ms // データとビットマップインデックスのロード時間。前のクエリと比較して、ビットマップインデックス(18MB)のロードに追加で400msかかりました。
DictDecode: 329.696ms // 出力行数が同じであるため、低基数最適化のための辞書デコード時間は同様です。
BitmapIndexFilter: 419.308ms // ビットマップインデックスによるデータフィルタリングの時間。
BitmapIndexFilterRows: 123.433M (123432975) // ビットマップインデックスによってフィルタリングされた行数。
ZoneMapIndexFiter: 171.580ms // ZoneMapインデックスによるデータフィルタリングの時間。
StarRocksのデフォルト設定に基づいてビットマップインデックスを使用するかどうかを判断
クエリ:
SELECT count(1) FROM lineorder_with_index WHERE lo_shipmode="MAIL";