openGemini:列存引擎解析

原创
2023/12/05 17:05
阅读数 20

列存引擎诞生的背景

在运维监控领域的某些场景中,可能存在某些标签的基数非常高的场景,如下是网络流量监控的一个样例数据:

img

其中,ip 地址、时间列就是典型的高基数列。

openGemini 现有的时序存储引擎在存储数据时,首先将数据按照时间线(即tag value的组合)进行聚簇,在时间线内再按照时间对数据进行排序,同时构建了 tag key/value 到时间线的倒排索引,这种存储方式在时间线数量相对有限的情形下,可以提供极致的写入与查询性能,但是在处理上述高基数场景时,时间线数量巨大,由于倒排索引与时间线数量相关,可能导致内存膨胀、读写性能下降等问题:

image-20231124114019723

image-20231124114028751

针对上述问题,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 的整体架构如下:

img

除了本文介绍的列存引擎以及后台 Compaction 以外,对查询引擎、接口协议也做了相应的适配,以达成更好的写入与查询性能,后续也会进一步分享相关技术。

列存引擎的使用

引入列存引擎之后,需要在创表的时候显式指定引擎类型,默认引擎类型为原有的时序引擎。创表的 DDL 命令如下:

CREATE MEASUREMENT $mst_name ($columnlists)
WITH ENGINETYPE = COLUMNSTORE 
[SHARDKEY $shardkeylist]
[TYPE HASH|RANGE]
[PRIMARYKEY $primarykeylist]
[SORTKEY $sortkeylist]

其中,关键字意义如下(上述出现的大写单词均为关键字,使用时不区分大小写) :

  • $mst_name 为创建的表名,实际使用时用具体名称替换。不支持包含 ,:;/\ 等特殊字符,如需包含其他特殊字符,需要使用双引号包含 mst_name

    CREATE 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 中
  • ENGINETYPE 表示引擎类型,时序引擎为 TSSTORE,列存引擎为 COLUMNSTORE

  • SHARDKEY 关键字指定存储引擎按给定的一个或多个字段进行数据分区打散,默认按全部 TAG KEYS 进行分区打散

  • TYPE 关键字表示打散方式,分为 HASH 和 RANGE 两种。默认为 HASH

  • PRIMARYKEY 关键字指定索引列,可以是一个或者多个字段,意味着存储引擎会在这些字段之上创建索引。

  • SORTKEY 指定存储引擎内部的数据排序方式。PRIMARYKEY 和 SORTKEY 二者关系是,PRIMARYKEY 需为 SORTKEY 的左前缀,否则报错,如果只配置了其中一个,则二者保持一致。

性能提升一览

列存引擎在高基数场景下可以带来显著的性能提升,以下对比了 InfluxDB、ClickHouse 的写入与查询性能。

image-20231124143552755

image-20231124143600626

其中,查询场景为:

  • 场景一:查询15分钟实时流量数据(时间范围查询)

  • 场景二:指定条件查询15分钟内实时流量数据

  • 场景三:查看低基标签列表(show tag values)

  • 场景四:查看高基标签列表(show tag values)

  • 场景五:全量数据统计查询(count *)

总结

openGemini 在现有时序引擎的基础上,通过调整数据排序与索引方式,推出了列存引擎,以解决高基数带来的问题,后续在此基础上,会提供更多丰富的索引类型,以加速不同场景的查询性能。


openGemini官网:http://www.openGemini.org

openGemini开源地址:https://github.com/openGemini

openGemini公众号:

欢迎关注~ 诚邀你加入 openGemini 社区,共建、共治、共享未来!

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部