进阶用法

更新时间:
复制 MD 格式

本节介绍列存索引在生产环境中常用的高阶能力:复杂分区、JSON 字段展开、Schema 动态感知、增量列添加、热重建等。

说明

部分能力当前仅在 Lake 模式 下支持,例如 dynamicJsonColumns 的存量构建、dynamicSchema 的通配符列同步等。各能力在两种模式下的支持情况请参见存储模式

复杂分区表达式

当主键字段无法直接用于分区(例如主键是 UNIX 时间戳而非日期字符串),可以在普通分区表达式中包含函数调用,从主键推导出便于业务过滤的分区键。

示例表 events,主键为 id + create_timecreate_time 为 UNIX 秒级时间戳):

+------------+---------------+---------+----------------+
| TABLE_NAME | COLUMN_NAME   |  TYPE   | IS_PRIMARY_KEY |
+------------+---------------+---------+----------------+
| events     | id            | BIGINT  | true           |
| events     | create_time   | BIGINT  | true           |
| events     | payload       | VARCHAR | false          |
| events     | source        | VARCHAR | false          |
+------------+---------------+---------+----------------+

将时间戳字段 create_time 转换为日期字符串后作为普通分区键,按天分区:

CREATE INDEX events_idx USING COLUMNAR ON events(*)
PARTITION BY ENUMERABLE (
  ifnull(substring(from_unixtime(create_time), 0, 10), 'unknown') AS dt,
  bucket(128, id)
)
WITH (
  `lindorm_columnar.user.index.database` = 'my_index_db',
  `lindorm_columnar.user.index.table`    = 'events_index'
);

查询时通过分区字段 dt 过滤,即可命中按天生成的分区:

SELECT /*+ _use_ldps_ */ COUNT(1)
FROM lindorm_columnar.my_index_db.events_index
WHERE dt = '2026-08-10';

仅为增量数据构建列存索引

如果不希望回查存量数据,只对增量数据构建索引,配置 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}');

静态展开存储

通过 lindorm_columnar.user.syncer.lci.jsonMapping. 定义 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'
);
  • a.b.c VARCHAR, a.e BOOLEAN, f DOUBLE 定义每一个展开字段,使用英文逗号分隔。

  • 字段名:JSON 路径,使用半角句号(.)分隔。

  • 字段类型:支持 BOOLEAN、BYTE、SHORT、INTEGER、LONG、FLOAT、DOUBLE、VARCHAR。

  • 可以通过多个 lindorm_columnar.user.syncer.lci.jsonMapping. 配置多个 JSON 字段的映射。

  • 同一个 JSON 字段不能同时定义在静态展开和动态展开中。

动态展开存储

通过 lindorm_columnar.user.syncer.lci.dynamicJsonColumns 声明需要动态展开的 JSON 列,列存索引会根据 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'
);
  • 支持多个动态 JSON 列,用英文逗号分隔,例如 json_col1,json_col2

  • 自动推断的字段类型仅包括 BOOLEAN、LONG、DOUBLE、STRING。同一字段出现多种类型时,会退化为 STRING。

  • 不支持对存量数据构建时的动态 JSON 展开:动态展开仅在增量阶段生效。

  • 同一 JSON 字段不能同时定义在静态展开和动态展开中。

JSON 展开字段命名规则

当为 JSON 字段配置了静态或动态展开后:

  • 默认行为:原始 JSON 字段不再进入列存表;展开字段命名以对应 JSON 字段名为前缀,例如 json_col.a.b.cjson_col.a.e

同步原始 JSON 字段

若希望同时保留原始 JSON 字段,配置 lindorm_columnar.user.syncer.lci.json.syncOriginalJsonContent = 'true'

  • json_col:STRING 类型,保存 JSON 原始内容。

  • json_col.a.b.cjson_col.a.e:展开后的字段。

忽略 JSON 展开字段的前缀

若希望展开字段不带 JSON 字段名前缀,配置 lindorm_columnar.user.syncer.lci.json.ignoreJsonMappingPrefix = 'true'

  • 展开后的列名将直接命名为 a.b.ca.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'
);
重要
  • 若主表启用了动态列通配符列,并且创建索引时指定了 dynamicSchema=true,则动态列和通配符列会同步至列存索引。通配符列同步当前仅在 Lake 模式下支持,OLAP 模式会跳过通配符列并记录警告。

  • 动态 Schema 会自动跟随主表列增减,因此不建议对同一张表频繁执行 DDL 变更以避免影响同步稳定性。

列过滤正则表达式

当只需要同步部分列时,通过 lindorm_columnar.user.syncer.lci.schema.columnRegex 指定正则表达式。该表达式与 column_name(,...) 列表是交集关系;主键列强制同步,无需在正则表达式中列出。

示例:宽表包含 c1, c2, c31, c32,仅同步匹配 (c1|c3.*) 的列(结果为 c1, c31, c32):

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.schema.columnRegex`      = '(c1|c3.*)'
);

后续主表新增的列如果匹配正则表达式,将自动加入列存索引。

为列存索引添加列

创建索引后,可以通过 ALTER INDEX 为列存索引增量添加普通字段JSON 静态映射字段,而无需重建索引。

示例:

-- 原表结构
-- my_json_tbl (id BIGINT PK, col1 INT, col2 VARCHAR, json_col1 JSON, json_col2 JSON)

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 静态映射字段
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 四种提取函数。

修改列过滤正则

对于已配置 columnRegex 的场景,可以通过 ALTER INDEX ... SET 更新正则表达式:

ALTER INDEX columnar_idx ON test_table SET
`lindorm_columnar.user.syncer.lci.schema.columnRegex` = '(c1|c3_(?!excludedColumn$).*|c3.*|c4.*)';

修改列存表表名

说明

此功能要求宽表引擎版本为 2.8.5.1 及以上。如何查看或升级版本,请参见宽表引擎版本说明升级小版本

列存索引处于 ACTIVE 状态后,可以通过 ALTER INDEX ... SET 修改列存表所在库表名:

ALTER INDEX columnar_idx ON test_table SET
`lindorm_columnar.user.index.database` = 'my_index_db',
`lindorm_columnar.user.index.table`    = 'my_new_index_tbl';

修改后列存表的库表名变为 my_index_db.my_new_index_tbl

列存索引热重建

ALTER INDEX 的变更仅对后续增量数据生效,不会重写历史数据。当您需要全量重建(例如新增一列并要求历史数据也可查询)时,直接删除后重建会造成较长的服务不可用窗口。通过并存两个索引 + 修改列存表表名,可以在服务无感的前提下完成热重建。

步骤如下:

  1. 保留旧索引columnar_idx_old):

        CREATE INDEX columnar_idx_old USING COLUMNAR ON test_table(id, col1)
        PARTITION BY ENUMERABLE (ifnull(id % 16, 0) AS dt, bucket(16, id))
        WITH (
          `lindorm_columnar.user.index.database` = 'my_index_db',
          `lindorm_columnar.user.index.table`    = 'my_old_index_tbl'
        );
  2. 创建新索引columnar_idx_new,增量一列 col2):

        CREATE INDEX columnar_idx_new USING COLUMNAR ON test_table(id, col1, col2)
        PARTITION BY ENUMERABLE (ifnull(id % 16, 0) AS dt, bucket(16, id))
        WITH (
          `lindorm_columnar.user.index.database` = 'my_index_db',
          `lindorm_columnar.user.index.table`    = 'my_new_index_tbl'
        );
  3. 等待 columnar_idx_new 变为 ACTIVE

  4. 删除旧索引

  5. 将新索引的列存表名切换为旧列存表名(业务查询 SQL 无需改动):

        ALTER INDEX columnar_idx_new ON test_table SET
        `lindorm_columnar.user.index.database` = 'my_index_db',
        `lindorm_columnar.user.index.table`    = 'my_old_index_tbl';

至此完成列存索引的热重建。