列存引擎诞生的背景
在运维监控领域的某些场景中,可能存在某些标签的基数非常高的场景,如下是网络流量监控的一个样例数据:
其中,ip 地址、时间列就是典型的高基数列。
openGemini 现有的时序存储引擎在存储数据时,首先将数据按照时间线(即tag value的组合)进行聚簇,在时间线内再按照时间对数据进行排序,同时构建了 tag key/value 到时间线的倒排索引,这种存储方式在时间线数量相对有限的情形下,可以提供极致的写入与查询性能,但是在处理上述高基数场景时,时间线数量巨大,由于倒排索引与时间线数量相关,可能导致内存膨胀、读写性能下降等问题:
针对上述问题,openGemini 在 v1.1.0 版本中,推出了全新的列存引擎(HSCE),以解决高基数带来的问题。
它如何解决高基数问题
openGemini 列存引擎解决高基数问题的核心思路是调整数据排序与索引方式,去掉与基数的关系。
假设业务数据如下:
Time | ispIp | direction | ISP | … | bps |
---|---|---|---|---|---|
7:05:40 | 192.69.3.132 | out | China Mobile | … | 56 |
7:05:40 | 192.69.3.177 | out | China Telecom | … | 66 |
7:05:41 | 192.69.3.132 | out | China Mobile | … | 76 |
… | … | … | … | … |
指定排序键为 ISP, direction, Time 等,那么数据按照排序键排序之后变成:
ISP | direction | Time | ispIp | … | bps |
---|---|---|---|---|---|
China Mobile | out | 7:05:40 | 192.69.3.132 | … | 56 |
China Mobile | out | 7:05:41 | 192.69.3.132 | … | 76 |
China Telecom | out | 7:05:40 | 192.69.3.177 | … | 66 |
… | … | … | … | … | … |
排序完成之后,将数据按列存储,存储时以若干行(如 8192 行)为一个 Block 进行数据压缩并序列化,在此基础上,选取每个 Block 的第一条记录构建稀疏的聚簇索引:
(稀疏)聚簇索引 |
---|
China Mobile, out, 7:05:40 |
… |
同时与时序引擎一样,使用 LSM-Tree 结构在后台对数据进行 Compaction,保持数据的整体有序性。
引入列存引擎后,openGemini 的整体架构如下:
除了本文介绍的列存引擎以及后台 Compaction 以外,对查询引擎、接口协议也做了相应的适配,以达成更好的写入与查询性能,后续也会进一步分享相关技术。
列存引擎的使用
引入列存引擎之后,需要在创表的时候显式指定引擎类型,默认引擎类型为原有的时序引擎。创表的 DDL 命令如下:
CREATE MEASUREMENT $mst_name ($columnlists) WITH ENGINETYPE = COLUMNSTORE [SHARDKEY $shardkeylist] [TYPE HASH|RANGE] [PRIMARYKEY $primarykeylist] [SORTKEY $sortkeylist]
复制
其中,关键字意义如下(上述出现的大写单词均为关键字,使用时不区分大小写) :
-
$mst_name 为创建的表名,实际使用时用具体名称替换。不支持包含
,:;/\
等特殊字符,如需包含其他特殊字符,需要使用双引号包含 mst_nameCREATE MEASUREMENT ":mst0" (tag1 TAG, field1 INT64 FIELD, field2 BOOL, field3 STRING, field4 FLOAT64)
复制 -
$columnlists 用于定义 Schema,包含在括号内,需要显式指定,暂不支持对 Schema 进行变更
(tag1 TAG, field1 INT64 FIELD, field2 BOOL, field3 STRING, field4 FLOAT64)
复制columnlists 指定了每一列的列名、数据类型以及列属性,其中
- TAG不需要指定数据类型,默认为
STRING
- 列属性可选值为
TAG
或者FIELD
,未指定列属性时,默认为FIELD
- 数据类型仅支持
FLOAT64
,INT64
,BOOL
,STRING
- 默认带
time
列,不需要包含在 columnlists 中
- TAG不需要指定数据类型,默认为
-
ENGINETYPE 表示引擎类型,时序引擎为 TSSTORE,列存引擎为 COLUMNSTORE
-
SHARDKEY 关键字指定存储引擎按给定的一个或多个字段进行数据分区打散,默认按全部 TAG KEYS 进行分区打散
-
TYPE 关键字表示打散方式,分为 HASH 和 RANGE 两种。默认为 HASH
-
PRIMARYKEY 关键字指定索引列,可以是一个或者多个字段,意味着存储引擎会在这些字段之上创建索引。
-
SORTKEY 指定存储引擎内部的数据排序方式。PRIMARYKEY 和 SORTKEY 二者关系是,
PRIMARYKEY
需为SORTKEY
的左前缀,否则报错,如果只配置了其中一个,则二者保持一致。
性能提升一览
列存引擎在高基数场景下可以带来显著的性能提升,以下对比了 InfluxDB、ClickHouse 的写入与查询性能。
其中,查询场景为:
-
场景一:查询15分钟实时流量数据(时间范围查询)
-
场景二:指定条件查询15分钟内实时流量数据
-
场景三:查看低基标签列表(show tag values)
-
场景四:查看高基标签列表(show tag values)
-
场景五:全量数据统计查询(count *)
总结
openGemini 在现有时序引擎的基础上,通过调整数据排序与索引方式,推出了列存引擎,以解决高基数带来的问题,后续在此基础上,会提供更多丰富的索引类型,以加速不同场景的查询性能。
openGemini官网:http://www.openGemini.org
openGemini开源地址:https://github.com/openGemini
openGemini公众号:
欢迎关注~ 诚邀你加入 openGemini 社区,共建、共治、共享未来!