本节介绍列存索引在生产环境中常用的高阶能力:复杂分区、JSON 字段展开、Schema 动态感知、增量列添加、热重建等。
部分能力当前仅在 Lake 模式 下支持,例如 dynamicJsonColumns 的存量构建、dynamicSchema 的通配符列同步等。各能力在两种模式下的支持情况请参见存储模式。
复杂分区表达式
当主键字段无法直接用于分区(例如主键是 UNIX 时间戳而非日期字符串),可以在普通分区表达式中包含函数调用,从主键推导出便于业务过滤的分区键。
示例表 events,主键为 id + create_time(create_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.c、json_col.a.e。
同步原始 JSON 字段
若希望同时保留原始 JSON 字段,配置 lindorm_columnar.user.syncer.lci.json.syncOriginalJsonContent = 'true':
json_col:STRING 类型,保存 JSON 原始内容。json_col.a.b.c、json_col.a.e:展开后的字段。
忽略 JSON 展开字段的前缀
若希望展开字段不带 JSON 字段名前缀,配置 lindorm_columnar.user.syncer.lci.json.ignoreJsonMappingPrefix = 'true':
展开后的列名将直接命名为
a.b.c、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'
);列过滤正则表达式
当只需要同步部分列时,通过 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_boolean、json_extract_long、json_extract_double、json_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.*)';修改列存表表名
列存索引处于 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 的变更仅对后续增量数据生效,不会重写历史数据。当您需要全量重建(例如新增一列并要求历史数据也可查询)时,直接删除后重建会造成较长的服务不可用窗口。通过并存两个索引 + 修改列存表表名,可以在服务无感的前提下完成热重建。
步骤如下:
保留旧索引(
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' );创建新索引(
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' );等待
columnar_idx_new变为ACTIVE。删除旧索引:
将新索引的列存表名切换为旧列存表名(业务查询 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';
至此完成列存索引的热重建。