全部產品
Search
文件中心

Lindorm:列存索引

更新時間:Dec 06, 2025

索引是加速資料查詢的重要方法。列存索引可以增強寬表中海量資料的分析計算能力,主要適用於車連網與物聯網的裝置資訊統計、電商領域的資料分析、物流行業的訂單統計等情境。本文介紹列存索引的基礎用法和高階用法,協助您快速上手並進一步掌握列存索引。

前提條件

  • 已開通計算引擎。具體操作,請參見服務開通

  • 已開通LindormDFS,且LindormDFS的版本為4.0.0及以上版本。

  • 已開通寬表引擎,且寬表引擎的版本為2.5.0及以上版本。

注意事項

  • 列存索引不支援同步構建方式

  • 列存索引的構建耗時為15分鐘左右。如果後台索引構建任務的數量較多,業務資料量較大,那麼構建列存索引的用時可能更長。

  • 由於列存索引的構建過程中需要回查資料,從而產生讀取操作,如果執行個體已開啟冷熱分離功能,請務必關注冷儲存(容量型雲端儲存)的限流情況。如果冷儲存的讀取操作受到限流,將直接影響索引的構建效率,進而可能導致寫入操作出現反壓現象。

開通列存索引

  1. 登入Lindorm管理主控台

  2. 在頁面左上方,選擇執行個體所屬的地區。

  3. 实例列表頁,單擊目標執行個體ID或者目標執行個體所在行操作列的管理

  4. 在左側導覽列,選擇宽表引擎

  5. 單擊列存索引頁簽,並單擊立即开通

  6. 在彈出的對話方塊中單擊確定

快速入門

假設要對海量資料表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表示該行資料產生的日期,通常會按照天層級進行資料分析。

  1. 建立列存索引。列存索引能自動為您展開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欄位內容變化來動態擴充索引表。

  2. 查看索引狀態。

    SHOW INDEX FROM my_tbl;

    SHOW INDEX的使用方法及返回結果集說明,請參見SHOW INDEX

  3. 使用列存索引進行資料查詢分析。具體操作,請參見使用列存索引

基礎用法

假設樣本表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分區運算式中的欄位均為寬表主鍵欄位。

  • 普通分區運算式

    • 可以指定0個或多個普通分區運算式,多個運算式用英文逗號(,)分隔。

    • 普通分區運算式為寬表主鍵欄位(例如城市、日期等),索引資料會按照不同的分區值構建,並在查詢時按照分區過濾條件高效定位元據。

  • bucket分區運算式

    • 至少指定1個bucket分區運算式。

    • bucket_num為bucket分區數目,column_name為bucket分區欄位,用於計算bucket分區號(bucket_index)。bucket_index的計算方法為基於運算式中的bucket分區欄位計算hash值,然後對bucket_num取餘得出。在下面的樣本中,bucket_index的計算方法為hash(pk0)%128

    • bucket分區欄位需為寬表中的主鍵欄位,並確保bucket分區欄位具有足夠的離散特徵,避免不同分區間的資料扭曲。

    普通分區運算式和bucket分區運算式共同決定了索引資料的分區數量,建議將每個分區的資料量設定在50 MB到512 MB之間。例如:普通分區運算式為日期欄位dt,寬表單日資料量為50 GB,寬表主鍵欄位為(id, dt),可以將分區運算式配置為PARTITION BY ENUMERABLE (dt, bucket(200, id))

說明

在設計分區策略時,應謹慎將高基數欄位(即具有大量唯一值的欄位)用作普通分區鍵。如果直接將其作為普通分區運算式會產生大量分區,形成海量小檔案,對儲存系統造成影響。

WITH(`key` = 'value')

使用WITH關鍵字為列存索引指定以下參數:

  • lindorm_columnar.user.index.database:指定列存索引表所在Database名稱。

  • lindorm_columnar.user.index.table:指定列存索引表名稱。

樣本

以表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的結構如下:

單擊查看JSON列資料結構

{
  "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_col1json_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');
重要

如果主表建立了動態列萬用字元列,且在建立列存索引時指定了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');

此時,在列存表中未包含 col2json_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_booleanjson_extract_longjson_extract_doublejson_extract_string提取函數。

常見問題

  • Q:建立列存索引後,是否會產生額外費用?

    A:會。主要包括列存索引資料的儲存費用,以及主表和列存索引之間資料同步實際使用的CU費用。

  • Q:分區運算式中是否可以包含非主鍵欄位?

    A:不可以。分區運算式中的欄位必須全部為主鍵欄位。

  • Q:bucket分區運算式中,是否可以包含複雜分區運算式?

    A:不可以。bucket分區運算式中僅包括bucket_numbucket分區欄位。

  • 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