在ClickHouse中,String字符串类型相比其他数据类型而言,一个显著的差异是String类型的大小是不固定的。所以除了常规的列字段压缩手段之外,还延伸出了一些额外的优化思路。
在《ClickHouse原理解析与应用实践》(你没看错,这是最终敲定的书名)这本书的**数据定义**章节中,曾提过在一些场合可以使用Enum枚举类型代替String字符串,从而将其转换为长度固定、字节更小的数值类型,这样在存储开销、读取效率、分组、排序、去重等操作时都会得到优化。
其实本质上,这就是一种对低基数特征字段的优化思路,只不过枚举类型的使用场景比较苛刻,它要求这些数据预先可知,且能够穷举。那么对于不可预知、无法穷举的数据应该怎么优化呢?
于是,ClickHouse提供了一种修饰数据类型LowCardinality,专门针对低基数特征的字段进行优化。
虽然LowCardinality的初衷是为了优化String,但是一不做二不休,LowCardinality目前还可以支持Int、Date和DateTime类型。
(主要在String场景使用,优化效果更明显)
LowCardinality和Nullable类似,是一种修饰类型,需要和其他数据类型组合使用,例如:
LowCardinality(String)
它们也有相应的简写形式,例如:
StringWithDictionary 等同于 LowCardinality(String)
如果需要使用String以外的LowCardinality类型,需要设置
SET allow_suspicious_low_cardinality_types = 1
接下来用一个示例说明,新增一张数据表:
CREATE TABLE test2(
其中v1是普通的String类型,v2是经过优化的String类型,之后会用它们来进行比较。
查看表结构,可以看到StringWithDictionary本质是语法糖,最终字段类型还是LowCardinality的形式:
ch7.nauu.com :) desc test2;
写入3亿行测试数据:
INSERT INTO test2
现在我们来分析一下LowCardinality有何其妙之处。
第一个最直观的感受是压缩率更高了,从下面结果可知,在这份数据下v2字段的压缩率提高了一倍:
SELECT
第二个直观感受是查询变快了,查询普通String:
ch7.nauu.com :) SELECT v1, count() FROM test2 GROUP BY v1 ORDER BY v1;
查询 LowCardinality:
ch7.nauu.com :) SELECT v2, count() FROM test2 GROUP BY v2 ORDER BY v2;
查询耗时也缩短了一倍时间。
那么LowCardinality背后的原理是什么呢? 其实从StringWithDictionary的名字已经很明显了,它是通过字典压缩编码进行优化的。
在默认的情况下,声明了LowCardinality的字段会基于数据生成一个全局字典,并利用倒排索引建立Key和位置的对应关系。如果数据的基数大于 8192,也就是说不同的值多于8192个,则会将一个全局字典拆分成多个局部字典(由 low_cardinality_max_dictionary_size 参数控制, 默认8192)。
因为进一步使用了字典压缩,所以查询的IO压力变小了,这是一处优化; 其次在处理数据的某些场合,可以直接使用字典进行操作,不需要将数据全部展开。
由于字典压缩和数据特征息息相关,所以这项特性的最终受益效果,需要在大家各自的环境中进行验证。通常来说,在百万级别基数的数据下,使用LowCardinality的收益效果都是不错的。
如果这篇文章对你有帮助,欢迎 订阅、转发、在看 三连击 :)
欢迎大家扫码关注我的 公众号和视频号 :
ClickHouse的秘密基地
nauu的奇思妙想
往期精彩推荐:
4. 在DB-Engines的排名不高,ClickHouse还值得关注吗?
6. 一分钟视频解读ClickHouse MergeTree
13.ClickHouse分布式IN & JOIN 查询的避坑指南
本文分享自微信公众号 - ClickHouse的秘密基地(chcave)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。