列存索引新版

更新时间:
复制为 MD 格式

索引是加速数据查询的重要方法。列存索引可以增强宽表中海量数据的分析计算能力,主要适用于车联网与物联网的设备信息统计、电商领域的数据分析、物流行业的订单统计等场景。本文介绍列存索引新版的基础用法,帮助您快速上手并进一步掌握列存索引。

重要

列存索引新版目前处于邀测阶段,如有使用需求,请联系Lindorm技术支持(钉钉号:s0s3eg3)申请使用。

核心优势

相比旧版列存索引,新版的核心优势如下:

特性

旧版

新版

同步延迟

15分钟

实时(秒级)

架构介绍

image

适用场景

  • 车联网:车辆轨迹实时分析

  • 电商:订单实时统计

  • 物流:运单实时监控

  • IoT:设备数据实时聚合

快速开始

前提条件

需开通以下引擎,并满足版本要求:

引擎

版本要求

作用

宽表引擎

>= 2.8.6

源数据存储

LTS

>= 3.9.1

日志实时订阅

列存引擎

>= 3.10.15

索引数据存储

计算引擎

-

执行分析查询

快速示例

假设要对海量数据表 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          |
+------------+-------------+---------+----------------+
  • pk0:数据标识,具有较大基数,适合作为 bucket 分区键。

  • pt_d:数据产生的日期,通常按天进行分析,适合作为枚举分区键。

操作步骤

  1. 通过Lindorm-cli连接并使用宽表引擎

  2. 参考以下代码示例,创建表和列存索引、查看索引状态、使用列存索引进行分析查询。

-- 1. 创建数据库并切换
CREATE DATABASE my_db;
USE my_db;

-- 2. 创建宽表
CREATE TABLE my_tbl (
  pk0 INT,
  pk1 VARCHAR,
  pt_d VARCHAR,
  col0 INT,
  col1 VARCHAR,
  PRIMARY KEY (pk0, pk1, pt_d)
);

-- 3. 创建列存索引
CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(pk0, pk1, pt_d, col0, col1)
PARTITION BY ENUMERABLE (pt_d, bucket(16, pk0))
WITH (
  `lindorm_columnar.user.index.database` = 'my_db',
  `lindorm_columnar.user.index.table` = 'my_tbl',
  `lindorm_columnar.user.index.type` = 'LCE'
);

-- 4. 查看索引状态
SHOW INDEX FROM my_tbl;

-- 5. 使用列存索引进行分析查询
SELECT /*+ _use_ldps_(cg_name), _columnar_index_ */ 
  pk1, SUM(col0) 
FROM my_db.my_tbl 
WHERE pt_d = '2024-01-01'
GROUP BY pk1;

创建索引

语法

CREATE INDEX index_name USING COLUMNAR ON table_name(column_list)
PARTITION BY ENUMERABLE (partition_expression)
WITH (properties);

参数说明

参数

说明

index_name

索引名称。

table_name

宽表名称。

column_list

索引列,详见索引列

partition_expression

分区表达式,详见分区配置

properties

索引属性,详见属性配置

索引列

索引列必须包含宽表的全部主键字段。

支持的数据类型:TINYINT、SMALLINT、INTEGER、BIGINT、LONG、FLOAT、DOUBLE、VARCHAR、BINARY、VARBINARY、BOOLEAN、DECIMAL、JSON、DATE、TIMESTAMP。

其中 JSON 类型在列存侧存储为 VARCHAR。

分区配置

分区表达式用于指定索引数据按照枚举算法进行分区,提升查询时的检索效率。分区表达式包括枚举分区表达式bucket 分区表达式

重要

分区表达式中的分区键,必须来自于宽表主键列。

枚举分区表达式

可指定 0 个或多个,多个表达式用英文逗号(,)分隔。

枚举分区表达式通常选用城市、日期等主键字段,索引数据会按照不同的分区值构建,查询时可根据分区过滤条件高效定位数据。

bucket 分区表达式

至少指定 1 个。语法为 bucket(bucket_num, column_name)

  • bucket_num:bucket 分区数目。

  • column_name:bucket 分区字段,用于计算 bucket 分区号。

bucket 分区号的计算方式:bucket_index = hash(column_name) % bucket_num

bucket 分区字段需为宽表主键字段,且应具有足够的离散特征,避免数据倾斜。

分区数量规划

枚举分区表达式和 bucket 分区表达式共同决定索引数据的分区数量,建议将每个分区的数据量控制在 50 MB ~ 512 MB 之间。

示例:宽表主键为 (uid, dt),单日数据量约 50 GB,可配置为:

PARTITION BY ENUMERABLE (dt, bucket(200, uid))
说明

设计分区策略时,应避免将高基数字段(具有大量唯一值的字段)用作枚举分区键,否则会产生大量分区,形成海量小文件,对存储系统造成影响。

属性配置

属性

必填

说明

lindorm_columnar.user.index.type

必须设置为 'LCE'

lindorm_columnar.user.index.database

列存引擎目标库名

lindorm_columnar.user.index.table

列存引擎目标表名

lindorm_columnar.user.syncer.skip.fullsync

'true':仅同步增量数据

重要

lindorm_columnar.user.index.type = 'LCE' 为必填项,缺失将走旧版链路。

数据同步

模式

配置

说明

全量+增量

默认

同时同步存量数据和增量数据

仅增量

lindorm_columnar.user.syncer.skip.fullsync='true'

跳过存量,仅同步增量数据

仅增量同步示例

CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(pk0, pk1, pt_d, col0, col1)
PARTITION BY ENUMERABLE (pt_d, bucket(128, pk0))
WITH (
  `lindorm_columnar.user.index.database` = 'my_db',
  `lindorm_columnar.user.index.table` = 'my_tbl',
  `lindorm_columnar.user.index.type` = 'LCE',
  `lindorm_columnar.user.syncer.skip.fullsync` = 'true'
);

JSON 字段展开

使用 json_extract 函数将 JSON 字段展开为独立列,便于在列存引擎中进行高效查询和分析。

支持的函数

函数

返回类型

说明

json_extract_string

STRING

提取字符串类型的值

json_extract_long

LONG

提取长整型数值

json_extract_double

DOUBLE

提取双精度浮点数值

json_extract_boolean

BOOLEAN

提取布尔类型的值

说明

如果 JSON 字段中指定路径不存在,或数据类型与函数不匹配,则返回 null

示例

假设宽表 my_tbl 包含 JSON 列 json_col,其内容结构如下:

{
  "a": {
    "b": {
      "c": "hello,world",
      "d": 123
    },
    "e": false
  },
  "f": 3.14
}

a.b.ca.b.da.ef 展开为独立列:

CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(
  pk0, pk1, pt_d,
  json_extract_string(json_col, '$.a.b.c'),
  json_extract_long(json_col, '$.a.b.d'),
  json_extract_boolean(json_col, '$.a.e'),
  json_extract_double(json_col, '$.f')
)
PARTITION BY ENUMERABLE (pt_d, bucket(16, pk0))
WITH (
  `lindorm_columnar.user.index.database` = 'my_db',
  `lindorm_columnar.user.index.table` = 'my_tbl',
  `lindorm_columnar.user.index.type` = 'LCE'
);

列名映射规则

JSON 字段展开后,列存引擎中的列名格式为 <JSON列名>.<字段路径>,类型由 json_extract 函数决定。以上示例生成的列如下:

列名

类型

说明

json_col.a.b.c

STRING

存储 json_col 字段中 a.b.c 的值。

json_col.a.b.d

LONG

存储 json_col 字段中 a.b.d 的值。

json_col.a.e

BOOLEAN

存储 json_col 字段中 a.e 的值。

json_col.f

DOUBLE

存储 json_col 字段中 f 的值。

保留原始 JSON 内容

默认情况下,使用 json_extract 展开字段时,仅同步展开后的列,原始 JSON 列不会同步到列存引擎。如需同时保留原始 JSON 内容,可设置以下属性:

`lindorm_columnar.user.syncer.lci.json.syncOriginalJsonContent` = 'true'

示例

展开字段的同时保留原始 json_col 列,便于后续查询完整 JSON 内容。

CREATE INDEX my_tbl_idx USING COLUMNAR ON my_tbl(
  pk0, pk1, pt_d, json_col,
  json_extract_string(json_col, '$.a.b.c'),
  json_extract_long(json_col, '$.a.b.d')
)
PARTITION BY ENUMERABLE (pt_d, bucket(16, pk0))
WITH (
  `lindorm_columnar.user.index.database` = 'my_db',
  `lindorm_columnar.user.index.table` = 'my_tbl',
  `lindorm_columnar.user.index.type` = 'LCE',
  `lindorm_columnar.user.syncer.lci.json.syncOriginalJsonContent` = 'true'
);

查询索引

语法

通过 HINT 指定走列存索引查询,而非查询宽表原始数据。

SELECT /*+ _use_ldps_(cg_name), _columnar_index_ */ ...
FROM index_database.index_table
WHERE ...;

其中 cg_name 为计算引擎 OLAP 资源组的名称。

示例

-- 聚合查询
SELECT /*+ _use_ldps_(cg_name), _columnar_index_ */ 
  pk1, COUNT(*), SUM(col0)
FROM my_db.my_tbl
WHERE pt_d = '2024-01-01'
GROUP BY pk1;

-- 明细查询
SELECT /*+ _use_ldps_(cg_name), _columnar_index_ */ *
FROM my_db.my_tbl
WHERE pt_d = '2024-01-01' AND pk0 = 12345
LIMIT 100;

索引管理

查看索引

SHOW INDEX FROM table_name;

输出包含索引状态、同步进度等信息。

修改索引

添加列

ALTER INDEX index_name ON table_name ADD COLUMNS (col1, col2);

删除索引

DROP INDEX index_name ON table_name;

常见问题

  • Q:分区表达式中是否可以包含非主键字段?

    A:不可以。分区表达式中的字段必须全部为主键字段。

  • Q:bucket 分区表达式中,是否可以包含复杂分区表达式?

    A:不可以。bucket 分区表达式中仅包括bucket_numbucket分区字段。

  • Q:分区数目过大或过小会有什么影响?

    A:分区数目过大,会导致元数据膨胀,从而影响查询效率,因此建议单分区数据量大于 50MB,bucket分区表达式中的bucket_num小于 1024。分区数目过小,会影响数据读写吞吐或造成数据倾斜,建议单分区数据量小于 512MB。

  • Q:能否为同一个宽表创建多个列存索引?

    A:不能。一张宽表仅支持创建一个列存索引。

  • Q:宽表中数据因为 TTL 过期被清除后,列存索引数据是否会被自动清除?

    A:不会。

  • Q:是否支持删除索引列?

    A:不支持。