索引是加速資料查詢的重要方法。列存索引可以增強寬表中海量資料的分析計算能力,主要適用於車連網與物聯網的裝置資訊統計、電商領域的資料分析、物流行業的訂單統計等情境。本文介紹列存索引的基礎用法和高階用法,協助您快速上手並進一步掌握列存索引。
前提條件
已開通計算引擎。具體操作,請參見服務開通。
已開通LindormDFS,且LindormDFS的版本為4.0.0及以上版本。
已開通寬表引擎,且寬表引擎的版本為2.5.0及以上版本。
注意事項
開通列存索引
登入Lindorm管理主控台。
在頁面左上方,選擇執行個體所屬的地區。
在实例列表頁,單擊目標執行個體ID或者目標執行個體所在行操作列的管理。
在左側導覽列,選擇宽表引擎。
單擊列存索引頁簽,並單擊立即开通。
在彈出的對話方塊中單擊確定。
快速入門
假設要對海量資料表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、DECIMAL、JSON、DATE、TIMESTAMP。 說明 該欄位列表必須包含對應寬表的全部主鍵欄位,若您需要為全部欄位(主鍵和非主鍵)建立列存索引,可簡寫為 |
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參數,將查詢請求路由至計算引擎執行並使用列存索引加速查詢,從而提升巨量資料計算的效率。
樣本一:巨量資料統計
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的結構如下:
靜態展開儲存
在建立列存索引時,您可以指定`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支援人員(DingTalk號:s0s3eg3)。
Q:能否為同一個寬表建立多個列存索引?
A:不能。一張寬表僅支援建立一個列存索引。
Q:寬表中資料因為TTL到期被清除後,列存索引資料是否會被自動清除?
A:不會。
Q:列存索引建立失敗了,再次建立為什麼會報錯?
A:一張寬表僅允許建立一個列存索引,無論該索引的狀態是否為失敗。您需要先刪除構建失敗的列存索引,再去建立新的索引。刪除列存索引的文法,請參見DROP INDEX。