索引是加速數據查詢的重要方法。列存索引可以增強寬表中海量數據的分析計算能力,主要適用于車聯網與物聯網的設備信息統計、電商領域的數據分析、物流行業的訂單統計等場景。本文介紹列存索引的基礎用法和高階用法,幫助您快速上手并進一步掌握列存索引。
前提條件
已開通列存索引功能。
重要列存索引功能需聯系Lindorm技術支持(釘釘號:s0s3eg3)開通。
已開通計算引擎。具體操作,請參見開通與變配。
已開通LindormDFS,且LindormDFS的版本為4.0.0及以上版本。
已開通寬表引擎,且寬表引擎的版本為2.5.0及以上版本。
注意事項
列存索引不支持同步構建方式。
列存索引的構建耗時為15分鐘左右。如果后臺索引構建任務的數量較多,業務數據量較大,那么構建列存索引的用時可能更長。
快速入門
假設要對海量數據表my_tbl
進行高效并行數據分析,您需要為該表創建列存索引。
示例表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
表示該行數據產生的日期,通常會按照天級別進行數據分析。
創建列存索引。列存索引能自動為您展開JSON類型字段中存儲的數據。
如果
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
的結構,來創建索引表。如果
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', `lindorm_columnar.user.syncer.lci.dynamicSchema` = 'true');
列存索引會根據您當前的表結構以及
json_col0
的結構,來創建索引表,并根據您后續的表結構、JSON字段內容變化來動態擴展索引表。
查看索引狀態。
SHOW INDEX FROM my_tbl;
SHOW INDEX
的使用方法及返回結果集說明,請參見SHOW INDEX。使用列存索引進行數據查詢分析。具體操作,請參見使用列存索引。
基礎用法
假設示例表my_tbl
的結構如下:
+------------+-------------+---------+----------------+
| TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY |
+------------+-------------+---------+----------------+
| my_tbl | pk0 | INT | true |
| my_tbl | pk1 | VARCHAR | true |
| my_tbl | pk2 | VARCHAR | true |
| my_tbl | col0 | INT | false |
| my_tbl | col1 | VARCHAR | false |
+------------+-------------+---------+----------------+
創建列存索引
語法
CREATE INDEX index_name USING COLUMNAR
ON table_name(column_name(,..))
PARTITION BY ENUMERABLE (column_name(,...), bucket(bucket_num, column_name))
WITH (`lindorm_columnar.user.index.database` = 'columnar_db_name',
`lindorm_columnar.user.index.table` = 'columnar_tbl_name');
參數說明
參數 | 說明 |
index_name | 列存索引的名稱,由大寫字母、小寫字母、數字、下劃線(_)其中的一種或多種組成。 |
table_name | 寬表名稱。 |
column_name(,...)) | 需要創建列存索引的字段列表,多個字段用英文逗號(,)分隔。目前支持創建列存索引的字段類型包括:TINYINT、SMALLINT、INTEGER、BIGINT、LONG、FLOAT、DOUBLE、VARCHAR、BINARY、VARBINARY、BOOLEAN。 說明 該字段列表必須包含對應寬表的全部主鍵字段,若您需要為全部字段(主鍵和非主鍵)創建列存索引,可簡寫為 |
PARTITION BY ENUMERABLE(column_name(,...), bucket(bucket_num, column_name)) | 指定索引數據按照枚舉算法進行分區,從而提升查詢過程中的檢索能力。分區表達式包括普通分區表達式和bucket分區表達式,且普通分區表達式和bucket分區表達式中的字段均為寬表主鍵字段。
|
WITH(`key` = 'value') | 使用
|
示例
以表my_tbl
為例創建列存索引:
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
語句查看列存索引的狀態。SHOW INDEX
的使用方法及返回結果集說明,請參見SHOW INDEX。
使用列存索引
創建列存索引可以增強寬表海量數據的分析計算能力,您可以在SELECT查詢語句中指定相關HINT參數,將查詢請求路由至計算引擎執行并使用列存索引加速查詢,從而提升大數據計算的效率。HINT參數的詳細說明和使用方式,請參見通過HINT方式使用資源組。
示例一:大數據統計
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
的使用方法及示例,請參見DROP INDEX。
進階用法
復雜分區表達式
假設示例表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
按天分區: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');
列存索引創建完成后,您可以在查詢語句中指定過濾條件并查詢列存索引中的數據。
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類型的字段展開存儲,支持靜態展開存儲與動態展開存儲兩種方式。
假設示例表my_json_tbl
的表結構如下:
+-------------+-------------+--------+----------------+
| 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 |
+-------------+-------------+--------+----------------+
執行以下語句插入JSON數據:
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_col
的結構如下:
{
"a": {
"b": {
"c": "hello,world",
"d": 123
},
"e": false
},
"f": 3.14
}
靜態展開存儲
在創建列存索引時,您可以指定`lindorm_columnar.user.syncer.lci.jsonMapping.<JSON_COL>` = '<JSON_MAPPING_RULE>'
,來定義寬表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');
lindorm_columnar.user.syncer.lci.jsonMapping.json_col:指定需要靜態展開的JSON列,此處指定列
json_col
。a.b.c VARCHAR,a.e BOOLEAN ,f DOUBLE
:指定每一個展開字段,使用英文逗號(,)隔開。字段名:展開字段對應的JSON路徑,使用半角句號(.)隔開。例如
a.b.c
。字段類型:支持的數據類型為BOOLEAN、BYTE、SHORT、INTEGER、LONG、FLOAT、DOUBLE和VARCHAR。
您可以通過
WITH
關鍵字指定多個lindorm_columnar.user.syncer.lci.jsonMapping
,為多個JSON字段創建映射。同一個JSON字段不能同時定義在靜態展開映射與動態展開映射中。
動態展開存儲(公測中)
在創建列存索引時,您可以指定`lindorm_columnar.user.syncer.lci.dynamicJsonColumns` = '<JSON_COL1>,<JSON_COL2>'
,來定義寬表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');
lindorm_columnar.user.syncer.lci.dynamicJsonColumns:指定需要動態展開的JSON列,此處指定列為
json_col
。支持指定多個動態展開的JSON列,使用英文逗號(,)隔開,例如json_col1,json_col2
。列存索引將根據JSON中實際存儲類型來推斷列存表的數據類型,推斷類型僅支持BOOLEAN、LONG、DOUBLE和STRING。如果您的數據值中有多種類型字段,則列存表使用STRING類型來存儲。
對于存量數據構建列存索引時,不支持JSON字段動態展開。
同一個JSON字段不能同時定義在靜態展開映射與動態展開映射中。
當您為JSON字段配置為靜態展開存儲或者動態展開存儲后,原始JSON字段將不會存儲到列存表中。同時,列存表存儲的JSON展開名會以其對應JSON字段名為前綴。
例如當您通過`lindorm_columnar.user.syncer.lci.jsonMapping.json_col` = 'a.b.c VARCHAR, a.e BOOLEAN'
,為列存索引指定JSON靜態展開存儲后。此時列存表中將不會有名為json_col
的列,而會有以下列:
名為
json_col.a.b.c
的列:類型為STRING,存儲json_col
字段中a.b.c
的值。名為
json_col.a.e
的列:類型為BOOLEAN,存儲json_col
字段中a.e
的值。
如果您仍希望同步原始JSON字段,或不期望列存表存儲的JSON展開字段名以對應JSON字段名為前綴,可以通過以下方式實現:
同步原始JSON字段
如果您仍然需要同步原始JSON字段,請在創建索引時指定 `lindorm_columnar.user.syncer.lci.json.syncOriginalJsonContent` = 'true'
。此時,列存表中會有以下列:
名為
json_col
的列:類型為STRING,存儲json_col
字段的值。名為
json_col.a.b.c
的列:類型為STRING,存儲json_col
字段中a.b.c
的值。名為
json_col.a.e
的列:類型為BOOLEAN,存儲json_col
字段中a.e
的值。
指定列存表的字段名忽略JSON字段名前綴
如果您不期望列存表存儲的JSON展開字段名會以其對應JSON字段名為前綴,請在創建索引時指定`lindorm_columnar.user.syncer.lci.json.ignoreJsonMappingPrefix` = 'true'
。此時,列存表中會有以下列:
名為
a.b.c
的列:類型為STRING,存儲json_col
字段中a.b.c
的值。名為
a.e
的列:類型為BOOLEAN,存儲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');
為列存表添加列(公測中)
創建列存索引后,您可以通過ALTER INDEX
語法為列存索引添加字段而無需重建索引,支持為列存索引添加普通字段或者JSON字段靜態展開的Mapping列。
假設表my_json_tbl
的結構如下:
+-------------+-------------+---------+----------------+
| 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 |
+--------------+-------------+---------+----------------+
您可以通過以下SQL語句創建列存索引:
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');
此時,在列存表中未包含 col2
、json_col2
相關的列。您可以通過以下語句為列存索引添加普通字段:
ALTER INDEX IF EXISTS columnar_idx ON my_json_tbl ADD COLUMNS(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_extract_boolean
、json_extract_long
、json_extract_double
、json_extract_string
提取函數。
常見問題
Q:創建列存索引后,是否會產生額外費用?
A:會。主要包括列存索引數據的存儲費用,以及主表和列存索引之間數據同步實際使用的CU費用。
Q:分區表達式中是否可以包含非主鍵字段?
A:不可以。分區表達式中的字段必須全部為主鍵字段。
Q:bucket分區表達式中,是否可以包含復雜分區表達式?
A:不可以。bucket分區表達式中僅包括
bucket_num
和bucket
分區字段。Q:分區數目過大或過小會有什么影響?
A:分區數目過大,會導致元數據膨脹,從而影響查詢效率,因此建議單分區數據量大于50 MB,bucket分區表達式中的
bucket_num
小于1024。分區數目過小,會影響數據讀寫吞吐或造成數據傾斜,建議單分區數據量小于512 MB。Q:是否可以通過Lindorm計算引擎直接訪問列存索引數據?
A:可以。您需要先自定義索引表的名稱再通過計算引擎訪問列存索引數據。具體操作,請參見訪問列存數據。
重要請謹慎執行列存索引表的修改操作,如需修改列存索引表,請聯系Lindorm技術支持(釘釘號:s0s3eg3)。
Q:能否為同一個寬表創建多個列存索引?
A:不能。一張寬表僅支持創建一個列存索引。
Q:寬表中數據因為TTL過期被清除后,列存索引數據是否會被自動清除?
A:不會。
Q:列存索引創建失敗了,再次創建為什么會報錯?
A:一張寬表僅允許創建一個列存索引,無論該索引的狀態是否為失敗。您需要先刪除構建失敗的列存索引,再去創建新的索引。刪除列存索引的語法,請參見DROP INDEX。