ClickHouse 是近年来分析型数据库的热点,一向以快著称,很多其它以性能为卖点的分析型数据库也常常会用它作为一个对比标杆。很多用户碰到数据库运算性能问题时,也会考虑转向求助于 ClickHouse 解决
ClickHouse 确实是有过人之处,它的列式宽表速度很快,估计是压缩做得非常好。然而,除此之外,再无长处。希望用 ClickHouse 解决数据库计算性能问题的用户,大概率会失望的。
我们先拿 TPCH 100G 来测试 ClickHouse,在同样的硬件环境下和 Oracle 对比,这里只列出一个结果(时间单位:秒),完整的测试报告在 SPL 计算性能系列测试:TPCH 。
TPCH 算是比较基础的数据库性能测试,总体来讲不算很复杂,但也包含了一些 JOIN 和子查询,不全是简单的单表遍历。这情况下,ClickHouse 的表现非常差,有相当多的题跑不出来,还有个别题的 SQL 被认为过于复杂而无法执行。这方面甚至还不如 Oracle,Oracle 不是专业分析型数据库,速度会慢很多,但所有题都能跑出来。
具体原因很可能如上所述,ClickHouse 仅仅是把数据存储压缩做得很好,这会导致简单遍历速度很快,但对稍复杂的运算,它的优化能力相当拉胯,结果直接跑不出来。
希望用 ClickHouse 解决性能问题的用户,要先考察一下自己面临的计算任务的复杂度和 TPCH 相比如何。
我们继续用这套 TCPH 数据生成一个多列的宽表,再做 ClickHouse 最为擅长的多维分析计算,结果如下(时间单位:秒),完整测试报告见 SPL 计算性能系列测试:关联表及宽表 。
宽表遍历是 ClickHouse 擅长的场景,性能最好。但只要有多一点关联,ClickHouse 的运算性能就会急剧下降。关联再复杂后又会溢出,再一次验证了上述的原因分析。
看来,ClickHouse 的“快”,仅仅在于最简单的无关联单表遍历,这种“快”能适应的场景实在是太狭窄了。专门引进一个数据库仅仅做这么一点点事情,值得吗?
相比 ClickHouse 的“徒有虚名”,上面测试报告中提到的 esProc SPL 才是性能王者。
esProc SPL 也是开源软件,它是纯 Java 开发的,但在相当多的性能优化场景中却能远远跑赢 C++ 开发的 ClickHouse。
严格地说,esProc 并不是一个分析型数据库,不过它提供了高性能的存储格式(列存、压缩等)和相应的算法类库,可以完全取代分析型数据库的计算功能。
和市场上其它与 ClickHouse 竞争的数据库产品不同,esProc 没有再使用 SQL 语法,而是采用了更简洁的 SPL。这样才能克服 SQL 的缺陷,实现 SQL 难以甚至无法实现的高性能算法。这里有通俗的解释 快出数量级的性能是怎样炼成的 。而继续采用 SQL 体系的数据库,即便在某些局部能超越 ClickHouse,但仍然会受到 SQL 的局限,无法充分利用硬件资源跑出最好的性能。
我们再再把刚才测试报告中 esProc SPL 的性能列出和 ClickHouse 对比:
结果,esProc SPL 表现出来的性能明显优于 ClickHouse,所有题都能很快跑出来,对 ClickHouse 有全面的碾压优势。
数据量加大后,ClickHouse 在擅长的单个宽表遍历场景中确实更胜一筹,比 esProc SPL 更快。不过,大多数情况下采用宽表是为了规避低速的关联运算(以更大的存储量和更复杂的数据准备换取不做关联),而 esProc SPL 特有的关联优化方案能够跑出比 ClickHouse 宽表更快的速度,没有必要再生成宽表了。宽表丧失性能优势后,就只剩缺点,纯属多余。
对于单表上的无关联简单统计,ClickHouse 虽然更快,但也没有比 esProc 快出数量级(毕竟 CPU 和硬盘的动作就是那么快)。如果这时候可以利用任务特征来优化时,基于 ClickHouse 仍然会很难搞,SQL 无法实现很多优化逻辑,只能在上层用 C++ 继续编程才能实现,非常繁琐且困难。而 SPL 的可编程能力要强大得多,可以充分利用任务特征写出优化代码。
比如现代多维分析时几乎总会涉及到多指标统计,SPL 可以写出遍历复用算法,一次遍历计算出多个统计值,即便单指标计算比 ClickHouse 稍慢,多指标统计时就能大幅超出:
(完整测试报告 SPL 计算性能系列测试:多指标统计 )
对于更复杂的运算,比如漏斗运算,其复杂度已经无法再用 ClickHouse 做测试了,esProc SPL 当然不在话下 SPL 计算性能系列测试:漏斗分析 。
总结一下:esProc 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,结果用了 5 节点的集群环境下也跑了 30 分钟多,达不到期望。同样数据量,SPL 代码只用一个节点不到 6 分钟即可完成计算,超出了用户期望。考虑到硬件资源的差距,SPL 相当于比 ClickHouse 快了 25 倍以上。
(SPL 代码写在格子里,这和普通程序语言很不像,参考这里 写在格子里的程序语言 )
SQL 中的 DISTINCT 计算会涉及 HASH 和比对,数据量很大时计算量也会很大,然后还有自关联以及进一步的 COUNT(DISTINCT),都会严重拖累性能,而 SPL 可以充分利用 SQL 没有的有序分组和序号定位,有效避免复杂度很高的自关联和 DISTINCT 运算。虽然在存储效率上比 ClickHouse 并没有优势,Java 也会略慢于 C++,但仍然获得了数量级的性能提升。
最后,esProc SPL 到这里找:https://github.com/SPLWare/esProc。