它被设计成快速。在开发过程中,查询执行性能始终是重中之重,但也考虑了其他重要特性,如用户友好性、可扩展性和安全性,以便 ClickHouse 能够成为一个真正的生产系统。
“构建速度”,Alexey Milovidov(ClickHouse 首席技术官)
“构建速度” 来自 2022 年 6 月阿姆斯特丹 ClickHouse 会议的演讲。
“ClickHouse 性能优化秘诀” 来自 2019 年 12 月大数据技术大会的演讲,对同一主题进行了更技术的探讨。
是什么让 ClickHouse 如此快速?
架构选择
ClickHouse 最初被构建为一个原型,只为了做好一项任务:尽可能快地过滤和聚合数据。构建典型的分析报告需要做到这一点,而典型的 GROUP BY 查询就是这样做的。ClickHouse 团队做出了几个高级别的决策,这些决策结合在一起,使得完成这项任务成为可能。
**列存储:**源数据通常包含数百甚至数千列,而报告可能只使用其中几列。系统需要避免读取不必要的列,以避免代价高昂的磁盘读取操作。
**索引:**内存驻留的 ClickHouse 数据结构允许仅读取必要的列,以及这些列中仅必要的行范围。
**数据压缩:**将同一列的不同值存储在一起通常会导致更好的压缩率(与面向行的系统相比),因为在实际数据中,同一列对于相邻行的值通常相同或没有那么多不同的值。除了通用压缩之外,ClickHouse 还支持 专用编解码器,可以使数据更加紧凑。
**向量化查询执行:**ClickHouse 不仅将数据存储在列中,还以列的方式处理数据。这会导致更好的 CPU 缓存利用率,并允许使用 SIMD CPU 指令。
**可扩展性:**ClickHouse 可以利用所有可用的 CPU 内核和磁盘来执行单个查询。不仅在一台服务器上,而且在集群的所有 CPU 内核和磁盘上也是如此。
关注底层细节
但是许多其他数据库管理系统也使用了类似的技术。真正让 ClickHouse 脱颖而出的是**关注底层细节**。大多数编程语言都为大多数常见算法和数据结构提供了实现,但它们往往过于通用而无法有效。每个任务都可以被视为具有各种特征的景观,而不是仅仅抛出一个随机的实现。例如,如果您需要哈希表,以下是一些需要考虑的关键问题
- 选择哪个哈希函数?
- 冲突解决算法:开放寻址 与 链接?
- 内存布局:一个数组用于键和值,还是单独的数组?它将存储小值还是大值?
- 填充因子:何时以及如何调整大小?如何在调整大小时移动值?
- 值是否会被删除,如果会被删除,哪个算法会工作得更好?
- 我们需要使用位图进行快速探测、字符串键的内联放置、对不可移动值的支持、预取和批处理吗?
哈希表是GROUP BY
实现的关键数据结构,ClickHouse会针对每个特定查询自动选择30多种变体之一。
算法也是如此,例如,在排序时,您可能会考虑
- 要排序的是什么:数字数组、元组、字符串还是结构体?
- 所有数据是否完全在内存中可用?
- 我们需要稳定排序吗?
- 我们需要完整排序吗?也许部分排序或第n个元素就足够了?
- 如何实现比较?
- 我们是否正在对已经部分排序的数据进行排序?
依赖于其所处理数据特征的算法通常比通用算法表现更好。如果事先并不知道这些特征,系统可以尝试各种实现,并在运行时选择最有效的那个。例如,请参阅一篇关于ClickHouse中LZ4解压缩实现的文章。
最后但并非最不重要的是,ClickHouse团队始终关注互联网上声称他们想出了最佳实现、算法或数据结构来完成某些任务的人,并尝试将其应用。这些说法大多被证明是错误的,但偶尔您确实会发现一些珍宝。
- 在设计系统时牢记底层细节。
- 基于硬件能力进行设计。
- 根据任务需求选择数据结构和抽象。
- 为特殊情况提供专门化。
- 尝试新的,“最佳”算法,即您昨天阅读到的那些。
- 基于统计信息在运行时选择算法。
- 在真实数据集上进行基准测试。
- 在CI中测试性能回归。
- 测量和观察一切。