ClickHouse 具有 ROLAP、在线实时查询、完整的 DBMS、列式存储、不需要任何数据预处理、支持批量更新、拥有非常完善的 SQL 支持和函数、支持高可用、不依赖 Hadoop 复杂生态、开箱即用等许多特点。特别是它那夸张的查询性能,我想大多数刚接触 ClickHouse 的人也一定会因为它的性能指标而动容。在一系列官方公布的基准测试对比中,ClickHouse 都遥遥领先对手,这其中不乏一些我们耳熟能详的名字。
所有用于对比的数据库都使用了相同配置的服务器,在单个节点的情况下,对一张拥有133个字段的数据表分别在1000万、1亿和10亿三种数据体量下执行基准测试,基准测试的范围涵盖43项SQL查询。在1亿数据集体量的情况下,ClickHouse的平均响应速度是 Vertica 的2.63倍、InfiniDB的17倍、MonetDB 的27倍、Hive 的126倍、MySQL 的429倍以及 Greenplum 的10倍。详细的测试结果可以查阅https://clickhouse.yandex/benchmark.html。
很多用户心中一直会有这样的疑问,为什么 ClickHouse 这么快?因为ClickHouse 是列式存储数据库,所以快;也因为 ClickHouse 使用了向量化引擎,所以快。这些解释都站得住脚,但是依然不能消除全部的疑问。因为这些技术并不是秘密,世面上有很多数据库同样使用了这些技术,但是依然没有ClickHouse这么快。所以我想从另外一个角度来探讨一番ClickHouse的秘诀到底是什么。
首先向各位读者抛出一个疑问:在设计软件架构的时候,做设计的原则应该是自顶向下地去设计,还是应该自下而上地去设计呢?在传统观念中,或者说在我的观念中,自然是自顶向下的设计,通常我们都被教导要做好顶层设计。而ClickHouse 的设计则采用了自下而上的方式。ClickHouse 的原型系统早在2008年就诞生了,在诞生之初它并没有宏伟的规划。相反它的目的很单纯,就是希望能以最快的速度进行 GROUP BY 查询和过滤。他们是如何实践自下而上设计的呢?
着眼硬件,先想后做
首先从硬件功能层面着手设计,在设计伊始就至少需要想清楚如下几个问题。
在这样的硬件上,我们需要达到怎样的性能?包括延迟、吞吐量等。
我们准备使用怎样的数据结构?包括 String、HashTable、Vector 等。
选择的这些数据结构,在我们的硬件上会如何工作?
如果能想清楚上面这些问题,那么在动手实现功能之前,就已经能够计算出粗略的性能了。所以,基于将硬件功效最大化的目的,ClickHouse 会在内存中进行GROUP BY,并且使用 HashTable 装载数据。与此同时,他们非常在意 CPU L3级别的缓存,因为一次L3的缓存失效会带来70~100ns的延迟。这意味着在单核 CPU 上,它会浪费4000万次/秒的运算;而在一个32线程的 CPU 上,则可能会浪费5亿次/秒的运算。所以别小看这些细节,一点一滴地将它们累加起来,数据是非常可观的。正因为注意了这些细节,所以 ClickHouse 在基准查询中能做到1.75亿次/秒的数据扫描性能。
算法在前,抽象在后
常有人念叨:“有时候,选择比努力更重要。”确实,路线选错了再努力也是白搭。在 ClickHouse 的底层实现中,经常会面对一些重复的场景,例如字符串子串查询、数组排序、使用 HashTable 等。如何才能实现性能的最大化呢?算法的选择是重中之重。以字符串为例,有一本专门讲解字符串搜索的书,名为“Handbook of Exact String Matching Algorithms”,列举了35种常见的字符串搜索算法。各位猜一猜 ClickHouse 使用了其中的哪一种?答案是一种都没有。这是为什么呢?因为性能不够快。在字符串搜索方面,针对不同的场景,ClickHouse 最终选择了这些算法:对于常量,使用 Volnitsky 算法;对于非常量,使用 CPU 的向量化执行 SIMD,暴力优化;正则匹配使用 re2 和hyperscan 算法。性能是算法选择的首要考量指标。
勇于尝鲜,不行就换
除了字符串之外,其余的场景也与它类似,ClickHouse 会使用最合适、最快的算法。如果世面上出现了号称性能强大的新算法,ClickHouse 团队会立即将其纳入并进行验证。如果效果不错,就保留使用;如果性能不尽人意,就将其抛弃。
特定场景,特殊优化
针对同一个场景的不同状况,选择使用不同的实现方式,尽可能将性能最大化。关于这一点,其实在前面介绍字符串查询时,针对不同场景选择不同算法的思路就有体现了。类似的例子还有很多,例如去重计数 uniqCombined 函数,会根据数据量的不同选择不同的算法:当数据量较小的时候,会选择 Array 保存;当数据量中等的时候,会选择 HashSet;而当数据量很大的时候,则使用HyperLogLog 算法。
对于数据结构比较清晰的场景,会通过代码生成技术实现循环展开,以减少循环次数。接着就是大家熟知的大杀器—向量化执行了。SIMD 被广泛地应用于文本转换、数据过滤、数据解压和 JSON 转换等场景。相较于单纯地使用 CPU,利用寄存器暴力优化也算是一种降维打击了。
持续测试,持续改进
如果只是单纯地在上述细节上下功夫,还不足以构建出如此强大的ClickHouse,还需要拥有一个能够持续验证、持续改进的机制。由于 Yandex的天然优势,ClickHouse 经常会使用真实的数据进行测试,这一点很好地保证了测试场景的真实性。与此同时,ClickHouse 也是我见过的发版速度最快的开源软件了,差不多每个月都能发布一个版本。没有一个可靠的持续集成环境,这一点是做不到的。正因为拥有这样的发版频率,ClickHouse 才能够快速迭代、快速改进。
所以ClickHouse 的黑魔法并不是一项单一的技术,而是一种自底向上的、追求极致性能的设计思路。这就是它如此之快的秘诀。
本文摘编于《ClickHouse原理解析与应用实战》,经出版方授权发布。
#欢迎来留言#
你用过ClickHouse吗?
对此你怎么看?
CSDN携手【机械工业出版社】送出
《ClickHouse原理解析与应用实战》一本
截至7月17日12:00点
关于作者:
ClickHouse贡献者之一,ClickHouse布道者,资深架构师,腾讯云最具价值专家TVP,开源爱好者, 十多年IT从业经验,对大数据领域主流技术与解决方案有深入研究,擅长分布式系统的架构设计与整合。曾主导过多款大数据平台级产品的规划、设计与研发工作,一线实战经验丰富。现就职于远光软件股份有限公司,任大数据事业部平台开发部总经理。