Lindorm のワイドテーブルは、データを行指向フォーマットで格納します。これはポイント検索には効率的ですが、数百万行にわたる大規模な集約処理には遅くなります。一方、列ストアインデックスは、同じデータをコンピュートエンジン内に列指向フォーマットで格納し、大規模なワイドテーブルに対する高速な並列スキャン、集約、および結合を可能にします。主な利用シーンには、インターネット・オブ・ビークルズ(IoV)およびインターネット・オブ・シングス(IoT)ワークロードにおけるデバイステレメトリの集約、e コマース分析、物流注文統計などがあります。
前提条件
開始する前に、以下の条件を満たしていることを確認してください。
コンピュートエンジンが有効化されていること。詳細については、「サービスの有効化」をご参照ください。
LindormDFS が有効化されており、バージョン 4.0.0 以降であること。
ワイドテーブルエンジンが有効化されており、バージョン 2.5.0 以降であること。
注意事項
列ストアインデックスは同期的に構築できません。初期構築には約 15 分かかります。データ量が非常に大きい場合やバックグラウンドタスクの数が多い場合は、この時間がさらに延長されます。
インデックスを構築すると、ワイドテーブルに対する読み取り I/O が発生します。コールド・ホットデータ分離が有効になっている場合、コールドストレージの速度制限によってインデックスの構築が遅くなり、書き込みバックプレッシャーが発生する可能性があります。
各ワイドテーブルでは、列ストアインデックスを 1 つだけ作成できます。以前の作成試行が失敗した場合、新しいインデックスを作成する前に、失敗したインデックスを削除してください。
ワイドテーブル内の TTL 期限切れ行は、列ストアインデックスから自動的にパージされません。
列ストアインデックス機能の有効化
Lindorm コンソール にログインします。
左上隅で、インスタンスがデプロイされているリージョンを選択します。
インスタンス ページで、インスタンス ID をクリックするか、インスタンスの詳細を表示 を 操作 列からクリックします。
左側ナビゲーションウィンドウで、ワイドテーブルエンジン を選択します。
列指向インデックス タブをクリックし、今すぐ有効化 をクリックします。
表示されるダイアログで、OK をクリックします。
クイックスタート
この手順では、my_tbl という名前のワイドテーブルに列ストアインデックスを作成します。
+------------+-------------+---------+----------------+
| TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY |
+------------+-------------+---------+----------------+
| my_tbl | pk0 | INT | true |
| my_tbl | pk1 | VARCHAR | true |
| my_tbl | pt_d | VARCHAR | true |
| my_tbl | col0 | INT | false |
| my_tbl | col1 | VARCHAR | false |
| my_tbl | json_col0 | JSON | false |
+------------+-------------+---------+----------------+pk0 は各行を識別する高カーディナリティの列です。pt_d はデータ生成日付であり、分析は通常日単位で実行されます。
ステップ 1:インデックスの作成
テーブルスキーマの変更頻度に応じて、以下のいずれかの文を選択してください。
安定したスキーマ —
my_tblの列がほとんど変更されない場合に使用します。CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(*) PARTITION BY ENUMERABLE (pt_d, bucket(128, pk0)) WITH ( `lindorm_columnar.user.index.database` = 'my_index_db', `lindorm_columnar.user.index.table` = 'my_index_tbl', `lindorm_columnar.user.syncer.lci.dynamicJsonColumns` = 'json_col0' );インデックススキーマは、作成時の現在のテーブルスキーマおよび
json_col0の構造に基づいて固定されます。進化するスキーマ — 列が後から追加される可能性がある場合、
dynamicSchema = 'true'を指定します。CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(*) PARTITION BY ENUMERABLE (pt_d, bucket(128, pk0)) WITH ( `lindorm_columnar.user.index.database` = 'my_index_db', `lindorm_columnar.user.index.table` = 'my_index_tbl', `lindorm_columnar.user.syncer.lci.dynamicJsonColumns` = 'json_col0', `lindorm_columnar.user.syncer.lci.dynamicSchema` = 'true' );インデックスは、その後のスキーマ変更および新しい JSON コンテンツを自動的に追跡します。
ステップ 2:インデックスのステータス確認
SHOW INDEX FROM my_tbl;出力フィールドの説明については、「SHOW INDEX」をご参照ください。
ステップ 3:インデックスを用いたクエリ実行
クエリをコンピュートエンジン経由でルーティングするため、_use_ldps_ および _columnar_index_ のヒントワードを追加します。
SELECT /*+ _use_ldps_(cg0), _columnar_index_ */ COUNT(*), SUM(col0)
FROM my_index_db.my_index_tbl
WHERE pt_d = '2024-01-15'
GROUP BY pk1;列ストアインデックスの作成
パラメーター
| パラメーター | 説明 |
|---|---|
index_name | 列ストアインデックスの名前。大文字、小文字、数字、アンダースコアを含めることができます。 |
table_name | ワイドテーブルの名前。 |
column_name [, ...] | 含める列。プライマリキー列をすべて含める必要があります。(*) を使用すると、すべての列を含めることができます。サポートされる型:TINYINT、SMALLINT、INTEGER、BIGINT、LONG、FLOAT、DOUBLE、VARCHAR、BINARY、VARBINARY、BOOLEAN、DECIMAL、JSON、DATE、TIMESTAMP。 |
PARTITION BY ENUMERABLE(...) | 列挙アルゴリズムを用いてインデックスデータをパーティション分割し、効率的なフィルタリングを実現します。通常のパーティション式をゼロ個以上、バケットパーティション式を正確に 1 個指定できます。両方の式に含まれるすべての列は、ワイドテーブルのプライマリキー列である必要があります。 |
bucket_num | バケットパーティションの数。1024 未満である必要があります。バケットインデックスは hash(column_name) % bucket_num で計算されます。データスキューを回避するため、高カーディナリティの列を使用してください。 |
lindorm_columnar.user.index.database | 列ストアインデックステーブルを保持するデータベース。 |
lindorm_columnar.user.index.table | 列ストアインデックステーブルの名前。 |
パーティションサイズのガイドライン
各パーティションのサイズを 50 MB ~ 512 MB の範囲に設定してください。
小さすぎると(< 50 MB):多数の小さなファイルが生成され、ストレージパフォーマンスが低下します。
大きすぎると(> 512 MB):読み取り/書き込みスループットが低下し、データスキューが発生します。
高カーディナリティのプライマリキー列を通常のパーティションキーとして使用しないでください。各異なる値ごとに別々のパーティションが作成され、多数の小さなファイルが生成されます。通常のパーティションキーには、日付などの時間またはカテゴリフィールドを使用し、高カーディナリティの列はバケット式に割り当ててください。
たとえば、1 日あたりのデータ量が 50 GB で、プライマリキーが (id, dt) の場合:
PARTITION BY ENUMERABLE (dt, bucket(200, id))例
CREATE INDEX my_tbl_idx USING COLUMNAR
ON my_tbl(pk0, pk1, pk2, col0, col1)
PARTITION BY ENUMERABLE (pk1, pk2, bucket(128, pk0))
WITH (
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl'
);列ストアインデックスの表示
作成後、インデックスはワイドテーブルからデータを継続的に同期します。まず既存データを同期し、その後増分データを同期します。増分同期中は、インデックスは通常、プライマリテーブルより 1 時間以内の遅延となります。
SHOW INDEX FROM my_tbl;出力フィールドの説明については、「SHOW INDEX」をご参照ください。
列ストアインデックスを用いたクエリ実行
SELECT クエリをコンピュートエンジン経由でルーティングするには、_use_ldps_ および _columnar_index_ のヒントワードを追加します。これにより、列ストアインデックスを活用してクエリを高速化できます。
これらのヒントワードを指定しない場合、クエリはワイドテーブルに対して直接実行され、列ストアインデックスは使用されません。
集約
SELECT /*+ _use_ldps_(cg0), _columnar_index_ */ COUNT(*), SUM(col0), MIN(col0), MAX(col0)
FROM my_index_db.my_index_tbl
WHERE col0 > 100 AND col0 < 200 OR col0 > 500
GROUP BY pk1;並べ替え
SELECT /*+ _use_ldps_(cg0), _columnar_index_ */ pk0 + col0, pk1
FROM my_index_db.my_index_tbl
WHERE col0 > 100 AND col0 < 200 OR col0 > 500
ORDER BY pk1
LIMIT 100;複数のワイドテーブル間での結合
複数のワイドテーブルに列ストアインデックスが存在する場合、それらのインデックステーブルを介して結合できます。
SELECT /*+ _use_ldps_(cg0), _columnar_index_ */ *
FROM my_index_db.my_index_tbl0 AS t0
JOIN my_index_db.my_index_tbl1 AS t1
ON t0.pk0 = t1.pk0
AND t0.pk1 = t1.pk1
LIMIT 100;列ストアインデックスの削除
列ストアインデックスを削除するには、DROP INDEX を使用します。完全な構文および例については、「DROP INDEX」をご参照ください。
高度な使い方
複雑なパーティション式
プライマリキーをパーティションキーとして直接使用できない場合(UNIX タイムスタンプなど)は、パーティション式内で変換関数を使用できます。
サンプルテーブル my_ts_tbl:
+------------+-------------+---------+----------------+
| TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY |
+------------+-------------+---------+----------------+
| my_ts_tbl | id | INT | true |
| my_ts_tbl | ts | LONG | true |
| my_ts_tbl | col0 | VARCHAR | false |
| my_ts_tbl | col1 | INT | false |
+------------+-------------+---------+----------------+ts(UNIX タイムスタンプ)列を用いて日単位でパーティション分割します。
CREATE INDEX my_ts_idx USING COLUMNAR ON my_ts_tbl(*)
PARTITION BY ENUMERABLE (
ifnull(substring(from_unixtime(ts), 0, 10), 'unknown') AS dt,
bucket(128, id)
)
WITH (
`lindorm_columnar.user.index.database` = 'my_ts_index_db',
`lindorm_columnar.user.index.table` = 'my_ts_index_tbl'
);計算されたパーティション列 dt を用いて日単位でクエリを実行します。
SELECT /*+ _use_ldps_ */ COUNT(1)
FROM lindorm_columnar.my_ts_index_db.my_ts_index_tbl
WHERE dt = '2020-06-06';増分データのみをインデックス化
既存データをスキップし、新規書き込みのみをインデックス化するには、lindorm_columnar.user.syncer.skip.fullsync = 'true' を設定します。
CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(*)
PARTITION BY ENUMERABLE (pk1, pk2, bucket(128, pk0))
WITH (
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl',
`lindorm_columnar.user.syncer.skip.fullsync` = 'true'
);JSON 列の展開
列ストアインデックスは、同期中に JSON 列を型付き列にフラット化できます。2 つの方法が利用可能です。
| 方法 | 動作の仕組み | 推奨用途 |
|---|---|---|
| 静的展開 | インデックス作成時に JSON パスとその型を定義します。 | 構造が既知で安定している JSON |
| 動的展開(パブリックプレビュー) | 実際のデータからパスと型を推論します。 | 構造が進化または予測不可能な JSON |
同一の JSON 列を静的展開と動的展開の両方で設定しないでください。
サンプルテーブルおよびデータ:
+-------------+-------------+--------+----------------+
| TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY |
+-------------+-------------+--------+----------------+
| my_json_tbl | id | BIGINT | true |
| my_json_tbl | col1 | INT | false |
| my_json_tbl | json_col | JSON | false |
+-------------+-------------+--------+----------------+UPSERT INTO my_json_tbl (id, col1, json_col)
VALUES (2, 2, '{"a": {"b": {"c": "hello,world", "d": 123}, "e": false}, "f": 3.14}');静的展開
各 JSON パスとその対象型を指定するマッピングルールを定義します。
CREATE INDEX columnar_idx USING COLUMNAR ON my_json_tbl(*)
PARTITION BY ENUMERABLE (ifnull(id%16, 0) AS dt, bucket(16, id))
WITH (
`lindorm_columnar.user.syncer.lci.jsonMapping.json_col` = 'a.b.c VARCHAR, a.e BOOLEAN, f DOUBLE',
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl'
);マッピングルールの形式は <json_path> <type> [, ...] です。
json_path:ドット区切りの JSON 値へのパス(例:a.b.c)type:BOOLEAN、BYTE、SHORT、INTEGER、LONG、FLOAT、DOUBLE、VARCHAR のいずれか
複数の JSON 列をマッピングするには、各 JSON 列ごとに lindorm_columnar.user.syncer.lci.jsonMapping.<column> パラメーターを 1 つ追加します。
動的展開(パブリックプレビュー)
動的に展開する JSON 列を指定します。インデックスは実際のデータから型を推論します。
CREATE INDEX columnar_idx USING COLUMNAR ON my_json_tbl(*)
PARTITION BY ENUMERABLE (ifnull(id%16, 0) AS dt, bucket(16, id))
WITH (
`lindorm_columnar.user.syncer.lci.dynamicJsonColumns` = 'json_col',
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl'
);推論される型は、BOOLEAN、LONG、DOUBLE、STRING のみです。フィールドに複数の型の値が含まれている場合、インデックスは STRING を使用します。
動的展開は既存データには適用されず、増分書き込みのみを処理します。
展開後の列名
JSON 列が展開されると(どちらの方法でも)、元の JSON 列は列指向テーブルには格納されません。代わりに、各展開パスごとに列が作成され、その名前には JSON 列名がプレフィックスとして付与されます。
'a.b.c VARCHAR, a.e BOOLEAN' を json_col の静的マッピングとして使用した場合の結果:
| 列名 | 型 | 格納される値 |
|---|---|---|
json_col.a.b.c | STRING | json_col からの a.b.c |
json_col.a.e | BOOLEAN | 「json_col」からの a.e |
元の JSON フィールドの同期
元の JSON 列も格納する場合、syncOriginalJsonContent = 'true' を追加します。
WITH (
`lindorm_columnar.user.syncer.lci.jsonMapping.json_col` = 'a.b.c VARCHAR, a.e BOOLEAN',
`lindorm_columnar.user.syncer.lci.json.syncOriginalJsonContent` = 'true',
...
)これにより、展開された列に加えて、STRING 型の json_col 列が追加されます。
列指向テーブル内のフィールド名から JSON 列名のプレフィックスを省略
展開された列名から JSON 列名のプレフィックスを省略する場合、ignoreJsonMappingPrefix = 'true' を追加します。
WITH (
`lindorm_columnar.user.syncer.lci.jsonMapping.json_col` = 'a.b.c VARCHAR, a.e BOOLEAN',
`lindorm_columnar.user.syncer.lci.json.ignoreJsonMappingPrefix` = 'true',
...
)この場合、列指向テーブルでは a.b.c および a.e が使用され、json_col.a.b.c や json_col.a.e は使用されません。
異なる JSON 列(例:json_col1 および json_col2)が同一のパス(例:a.b.c)を展開・格納しようとする場合、列ストアインデックスの作成は失敗します。
スキーマ進化の動的トラッキング(パブリックプレビュー)
列指向テーブルのスキーマをプライマリテーブルと同期させるには、lindorm_columnar.user.syncer.lci.dynamicSchema = 'true' を設定します。ワイドテーブルにカラムが追加されると、それらは自動的にインデックスにも追加されます:
CREATE INDEX my_tbl_idx USING COLUMNAR
ON my_tbl(*)
PARTITION BY ENUMERABLE (pt_d, bucket(128, pk0))
WITH (
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl',
`lindorm_columnar.user.syncer.lci.dynamicSchema` = 'true'
);ワイドテーブルで動的カラムまたはワイルドカードカラムが使用されている場合、dynamicSchema = 'true' が有効になっていると、これらのカラムタイプもインデックスに同期されます。
インデックスの再作成を行わずに列を追加(パブリックプレビュー)
既存の列ストアインデックスに列を追加する際に、フルリビルドを実行せずに ALTER INDEX を使用します。
サンプルテーブル:
+-------------+-------------+---------+----------------+
| TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY |
+-------------+-------------+---------+----------------+
| my_json_tbl | id | BIGINT | true |
| my_json_tbl | col1 | INT | false |
| my_json_tbl | col2 | VARCHAR | false |
| my_json_tbl | json_col1 | JSON | false |
| my_json_tbl | json_col2 | JSON | false |
+-------------+-------------+---------+----------------+初期インデックス作成(id、col1、json_col1 を対象):
CREATE INDEX columnar_idx USING COLUMNAR ON my_json_tbl(id, col1, json_col1)
PARTITION BY ENUMERABLE (ifnull(id%16, 0) AS dt, bucket(16, id))
WITH (
`lindorm_columnar.user.syncer.lci.jsonMapping.json_col1` = 'a.b.c VARCHAR, a.e BOOLEAN',
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table` = 'my_index_tbl'
);通常の列を追加:
ALTER INDEX IF EXISTS columnar_idx ON my_json_tbl ADD COLUMNS (col2);json_col2 に対する静的 JSON マッピングを追加:
ALTER INDEX IF EXISTS columnar_idx ON my_json_tbl
ADD COLUMNS (
json_extract_long(json_col2, '$.key1'),
json_extract_boolean(json_col2, '$.key2'),
json_extract_double(json_col2, '$.key3.key4')
);サポートされる JSON 抽出関数: json_extract_boolean、json_extract_long、json_extract_double、json_extract_string。
課金
列ストアインデックスを作成すると、2 種類の課金が発生します。
ストレージ: 列ストアインデックスデータによって消費されるストレージ。
Compute Units (CUs): ワイドテーブルとインデックス間でデータを同期するために使用される CU。
よくある質問
列ストアインデックスを作成する際に追加料金は発生しますか?
はい。インデックスデータのストレージコストおよびワイドテーブルとインデックス間の同期に伴う CU コストが課金対象となります。
パーティションフィルター式には、プライマリキー列以外の列を含めることができますか?
いいえ。パーティションフィルター式(通常のパーティション式およびバケット式を含む)に含まれるすべての列は、ワイドテーブルのプライマリキー列である必要があります。
バケットパーティション式には計算ロジックを含めることができますか?
いいえ。バケットパーティション式では、bucket_num およびバケット列名のみが許可されます。変換関数(例: from_unixtime)は、通常のパーティション式でご使用ください。
パーティション数が多すぎたり少なすぎたりするとどうなりますか?
パーティション数が多すぎるとデータ肥大化が発生し、ストレージオーバーヘッドが増加してクエリ速度が低下します。各パーティションのサイズは 50 MB を超えるようにし、bucket_num は 1024 未満に保ってください。一方、パーティション数が少なすぎると読み取り/書き込みスループットが制限され、データスキューが発生します。各パーティションのサイズは 512 MB を下回るようにしてください。
Lindorm コンピュートエンジンを介して列ストアインデックスを直接クエリできますか?
はい。クエリ内でインデックステーブル名を指定し、_use_ldps_ ヒントワードを使用してください。設定方法の詳細については、「列ストアデータへのアクセス」をご参照ください。
列ストアインデックステーブルを直接変更する前に、Lindorm テクニカルサポート(DingTalk ID: s0s3eg3)にお問い合わせください。
同一のワイドテーブルに対して複数の列ストアインデックスを作成できますか?
いいえ。各ワイドテーブルは、列ストアインデックスを 1 つだけサポートします。
TTL により期限切れとなった行は、列ストアインデックスから自動的に削除されますか?
いいえ。ワイドテーブルにおける TTL の期限切れは、列ストアインデックスには反映されません。
インデックス作成に失敗しました。再度作成しようとしても失敗するのはなぜですか?
失敗状態のインデックスを含め、各ワイドテーブルは列ストアインデックスを最大 1 つしかサポートしません。DROP INDEX を使用して失敗したインデックスを削除した後、新しいインデックスを作成してください。