ClickHouse 为什么如此之快?
它被设计为快速。在开发过程中,查询执行性能始终是重中之重,但其他重要特性,如用户友好性、可扩展性和安全性也得到了考虑,以便 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 多种变体 之一。
算法也是如此,例如,在排序时,您可能会考虑
- 将要排序的是什么:数字、元组、字符串或结构的数组?
- 所有数据是否完全在 RAM 中可用?
- 我们需要稳定的排序吗?
- 我们需要完全排序吗?也许部分排序或第 n 个元素就足够了?
- 如何实现比较?
- 我们是否正在对已经部分排序的数据进行排序?
它们依赖于其正在处理的数据特征的算法通常比其通用对应算法表现更好。如果事先并不知道,系统可以尝试各种实现,并在运行时选择最有效的实现。例如,请参阅 关于如何在 ClickHouse 中实现 LZ4 解压缩的文章。
最后但并非最不重要的是,ClickHouse 团队始终监控互联网上人们声称他们想出了最好的实现、算法或数据结构来做某事,并尝试一下。这些说法大多被证明是错误的,但偶尔你确实会发现一块宝石。
- 在设计系统时牢记底层细节。
- 基于硬件功能进行设计。
- 根据任务需要选择数据结构和抽象。
- 为特殊情况提供专门化。
- 尝试昨天阅读到的新“最佳”算法。
- 基于统计信息在运行时选择算法。
- 在真实数据集上进行基准测试。
- 在 CI 中测试性能回归。
- 测量和观察一切。
ClickHouse 性能
根据开源分析数据库基准测试 (ClickBench) 的 基准测试结果,ClickHouse 表现出最佳性能——对于可比的操作场景,在可供测试的同类系统中,ClickHouse 同时展现了最快的长查询吞吐量和最短的短查询延迟。
许多独立的基准测试都得出了类似的结论。使用互联网搜索并不难找到它们,或者您可以查看 我们收集的一些相关链接。
单个大型查询的吞吐量
吞吐量可以用每秒行数或每秒兆字节数来衡量。如果数据位于页面缓存中,则在现代硬件上,不太复杂的查询的处理速度约为每秒 2-10 GB 的未压缩数据(对于最简单的案例,速度可能达到 30 GB/秒)。如果数据未放在页面缓存中,则速度取决于磁盘子系统和数据压缩率。例如,如果磁盘子系统允许以 400 MB/秒的速度读取数据,并且数据压缩率为 3,则预期速度约为 1.2 GB/秒。要获得每秒行数的速度,请将每秒字节数的速度除以查询中使用的列的总大小。例如,如果提取了 10 字节的列,则预期速度约为每秒 1 亿到 2 亿行。
对于分布式处理,处理速度几乎线性增加,但这仅在聚合或排序产生的行数不太大的情况下才成立。
处理短查询时的延迟
如果查询使用主键并且不选择太多要处理的列和行(数十万),则如果数据位于页面缓存中,您可以预期延迟小于 50 毫秒(在最佳情况下为几毫秒)。否则,延迟主要取决于搜索次数。如果您使用旋转磁盘驱动器,对于未过载的系统,可以使用以下公式估算延迟:搜索时间 (10 毫秒) * 查询的列数 * 数据部分数
。
处理大量短查询时的吞吐量
与传统数据仓库相比,ClickHouse 可以处理非常高的每秒查询数 (QPS)。在一台服务器上,它可以持续运行数百到数千次 QPS,具体取决于查询的复杂性。我们建议从每秒最多 100 个查询开始,然后根据需要调整此数字。
插入数据时的性能
我们建议以至少 1000 行的包插入数据,或者每秒不超过一个请求。从制表符分隔的转储中插入 MergeTree 表时,插入速度可以达到 50 到 200 MB/s。如果插入的行大小约为 1 KB,则速度将为每秒 50,000 到 200,000 行。如果行很小,则每秒的行数性能可能会更高(在 Banner 系统数据上 ->
每秒 500,000 行;在 Graphite 数据上 ->
每秒 1,000,000 行)。为了提高性能,您可以并行执行多个 INSERT 查询,这可以线性扩展。