ClickHouse 作为一种分析数据库,近年来很流行。它以速度快而闻名,经常被许多其他以高性能为卖点的分析数据库供应商用作基准。

很多用户在遇到数据库性能问题时会考虑转向ClickHouse。

ClickHouse有一个突出的特点。它的面向列的宽表运行速度很快,这可能要归功于出色的压缩。然而,它没有任何其他优点。希望用它来解决数据库性能问题的用户可能会感到失望。

我们在相同硬件环境下对比了ClickHouse和Oracle的TPC-H 100G性能。下面仅列出测试结果(时间单位:秒)。在此处查找完整的测试报告。

Image description
TPC-H数据库性能测试相对基础,一般来说并不复杂。但并不是所有的测试都是简单的单表遍历,还有JOIN和子查询。在这些情况下,ClickHouse 的性能非常差。

有相当多的计算任务根本无法完成;一些任务的SQL语句甚至被认为过于复杂而无法执行。 ClickHouse 在处理这些问题上甚至不如 Oracle。

Oracle不是专门的分析数据库,速度慢很多,但它可以完成所有任务。

具体原因大概就是上面提到的。

ClickHouse只是在数据存储压缩方面做得很好,这使得简单的遍历速度非常快;但对于稍微复杂的计算,它的优化能力相当有限,根本无法完成执行。

因此,想要使用ClickHouse解决性能问题的用户应该首先通过与TPC-H进行比较来检查其计算任务的复杂性。

现在我们仍然使用TPC-H数据来生成一个有很多列的表,并进行ClickHouse最擅长的多维分析。结果如下(时间单位:秒)。在此处查找完整的测试报告。

Image description
ClickHouse擅长处理涉及宽表遍历的场景,性能最高。然而,一旦涉及连接,它的性能就会急剧下降。更加复杂的join会导致内存溢出,这再次验证了上面的原因分析。

看来ClickHouse只有在处理不涉及任何关联的简单单表遍历时才快。但如此“快的速度”ClickHouse所能实现的应用场景太窄了。

仅仅为了完成这么简单的工作,值得专门引入一个数据库吗?

与名气夸张的ClickHouse不同,上面测试报告中提到的集算器SPL才是真正的性能冠军。

集算器SPL也是开源的。它是用Java开发的,但是在很多性能优化场景下都可以远远胜过C++开发的ClickHouse。

严格来说,集算器并不是一个分析型数据库。但它提供了高性能的存储模式(面向列的存储、压缩等)和相应的算法库,这使得它能够完全取代分析数据库的计算功能。

与ClickHouse的其他数据库竞争对手不同,集算器没有采用SQL语法,而是使用更简洁的SPL(结构化过程语言)。

该设计可以克服SQL的缺陷,帮助实现SQL难以或无法实现的高性能算法。在这里找到易于理解的插图。如果仍然使用基于SQL系统的数据库,即使在某些方面可以超越ClickHouse,产品仍然会受到SQL的限制,无法充分利用硬件资源来获得尽可能最好的性能。

我们将上述测试报告中集算器的性能与ClickHouse的性能进行比较:

Image description
集算器SPL在性能上明显超过ClickHouse,因为它执行所有任务的速度非常快。与ClickHouse相比,它具有全面的、压倒性的优势。

Image description
当数据量增长时,ClickHouse在处理其擅长的单宽表遍历方面确实比集算器SPL更好更快。

然而,由于大多数情况下使用宽表是为了避免低速连接操作(通过增加数据量和执行更复杂的数据准备),当集算器SPL独特的连接优化策略实现比ClickHouse宽表更高的速度时,宽表就变得不必要了。

宽表不再有任何优点,一旦失去性能优势就变得毫无用处。

虽然ClickHouse在处理单表上的简单非关联汇总方面速度更快,但它比集算器快不了几个数量级(毕竟CPU和硬盘也就这个速度)。

在这种情况下,即使可以利用任务特征进行优化,基于ClickHouse我们仍然会发现很难解决。很多优化逻辑无法在SQL中实现,需要更高层次的C++编程来完成目标。

这是复杂且困难的。而SPL的编程能力要强大得多,可以很好地利用任务特性来编写优化代码。

例如,当代的多维度分析几乎总是涉及多指标汇总。我们可以在 SPL 中编写多用途遍历算法,在一次遍历中计算多个聚合值。

即使SPL在处理单索引计算时比ClickHouse稍慢,但在处理多索引汇总时它可以明显超过它:

Image description
(在这里找到完整的测试报告)

对于更复杂的计算,例如漏斗分析,它们太复杂而无法使用 ClickHouse 进行测试。但集算器SPL可以测试它们。在此处查找相关信息。

简而言之,集算器SPL具有完整、全面的性能优势,但ClickHouse的性能优势只适用于非常狭窄的应用范围。

这是一个现实世界的用例。时空碰撞问题涉及约250亿行数据。看起来SQL语句并不复杂:

WITH DT AS ( SELECT DISTINCT id, ROUND(tm/900)+1 as tn, loc FROM T WHERE tm<3*86400)
SELECT * FROM (
    SELECT B.id id, COUNT( DISINCT B.tn ) cnt
    FROM DT AS A JOIN DT AS B ON A.loc=B.loc AND A.tn=B.tn
    WHERE A.id=a AND B.id<>a
    GROUP BY id )
ORDER BY cnt DESC
LIMIT 20

传统数据库运行速度太慢,用户转向ClickHouse来解决问题。 ClickHouse在5节点集群环境下运行超过30分钟,达不到预期。

同等数据量下,SPL代码一个节点,不到6分钟即可完成计算,超出了用户的预期。考虑到硬件资源的差距,SPL比ClickHouse快25倍以上。

Image description
(与其他编程语言生成的代码不同,SPL 代码是在网格中编写的。在此处查找相关信息)。

SQL DISTINCT 计算涉及 HASH 值及其比较。当数据量很大时,它会变得计算密集。自连接和进一步的 COUNT(DISTINCT) 也会严重降低性能。

SPL可以充分利用SQL不需要的有序分组和基于序数的定位,有效避免高复杂度的自连接和DISTINCT操作。

虽然SPL在存储效率方面并不比ClickHouse有优势,而且Java比C++慢一点,但它仍然使性能提升了几个数量级。