第一性原理
第一性原理这个概念大家应该不会陌生,它原本是由古希腊哲学家亚里士多德提出的,意指“在系统中会存在一个最基本的命题,它不能被违背或者删除”。
这个概念近些年被再一次的推崇起来是和埃隆马斯克有关,这位造电动车、造火箭、造宇宙飞船的硅谷钢铁侠声称他一直遵循着第一性原理的方法在思考问题。埃隆马斯克所说的第一性原理意指“思考问题首先应该向下一层层拨开表象,去发现事物的本质,再由本质一层层向上堆砌。”
我们不难发现,不论是哪种第一性原理,它们都表达出了一个相同的观点,即每个事物冥冥中都存在着一个核,我们需要看清它并基于这个核来思考。
如果把第一性原理类比到软件设计中,那么就可以这样理解,即每款软件被设计出来都需要解决一个最基本的问题,这就促成了该软件的基础功能, 也就是它的核,之后这款软件的其他功能都是通过基础功能向上一层层堆砌出来的。
从解决本质问题起源
那么 ClickHouse 的第一性原理是什么呢?ClickHouse 官方研发团队负责人 Alexey Milovidov 在一次分享中曾提过,ClickHouse 在设计之初并没有什么宏伟的规划蓝图,他们的目的很单纯,就是怎样能将自家业务数据的查询做到尽可能的快,换言之也就是想把 GROUP BY 查询做到尽可能快。
所以从第一性原理的角度来看,ClickHouse 要解决的最基本的问题是如何能够把 GROUP BY 做到尽可能的快,这才是他的本质。在明白了这一点之后我们再回过头来看待ClickHouse的一些功能,就能理解为什么它不像其他 OLAP 数据库一样,从一开始就提供了大而全的通用功能(如开窗函数、执行计划解析等)。因为这些周边功能不是 ClickHouse 需要解决的本质问题,它的本质就是要“变态”快,在解决好了这个问题之后再由此之上一层层的叠加周边的附属功能。
很显然,承载着ClickHouse 要实现“变态”快的媒介就是MergeTree,所以我们从 MergeTree 身上只会看到能够支撑“变态”快的数据结构和算法,例如:
相比使用一行索引标记对应一行数据的稠密索引,MergeTree 使用了稀疏索引,即一行索引标记对应一个区间的数据(默认8192行)。
每个列字段拥有一个独立的存储文件,并配有独立对应的索引标记文件;
数据一旦被写入就不会被更改,这样可以有效的将磁盘的随机写转化成顺序写。
每一次执行 INSERT写入数据的时候,MergeTree都会生成一个全新的数据段(part),同时通过后台线程不定期地将多个已经存在的数据段合并成一个新的数据段,这种不断合并(Merge)的特点也正是MergeTree名字的由来。这种方法在不变性的前提下可以实现数据写入和查询的无锁化,从而进一步降低了系统的实现难度并提高系统的性能。
关于更多MergeTree原理的细节,我会在后续的课程介绍,这里就不再赘述。总而言之,正是 MergeTree 的一些优秀设计和高效的实现,让ClickHouse 很好的解决了 Yandex(ClickHouse背后的俄罗斯公司)在线业务查询的性能问题。
由基础功能向外延伸
随着ClickHouse 被应用到Yandex内部更多的场景后,特别是当ClickHouse 开源了以后,它的使用受众变得更加广泛,所以ClickHouse需要面对的应用场景也随之变得更加千奇百怪,原本工作良好的MergeTree这时就出现了一些短板,我们可以将MergeTree面临的一些挑战分为这么几类。
MergeTree 面临的第一个挑战是不支持唯一性约束。虽然MergeTree也有主键的概念,但是它的主键并不支持唯一性约束的语义,也就是说即便两条数据的主键相同,它们仍然可以被正常写入。这种设计初看起来会觉得很奇怪,但是在知道了第一性原理之后也就可以理解了。MergeTree 的目标是为了“变态快”,所以那些会影响到写入性能的功能通通不在考虑范畴之内。之所以还会设计主键的功能,也完全只是为了服务于生成一级索引(稀疏索引)的目的,与其他因素无关。
道理大家都能理解,但是现实的业务诉求是真真切切存在的。在ClickHouse所应对的大数据分析场景下,它的上游系统通常也会是一个分布式系统。负责数据写入的调度任务也经常会有失败的可能,所以通常都要求这类任务在设计实现时具有幂等性。当任务失败的时候会尝试重新执行,但是最终的结果要求是一致的。MergeTree无法支持这种幂等性的要求,如果相同的数据写入任务被重复执行,那么它就会存在重复的数据。
MergeTree 面临的第二个挑战是不支持行级的数据更新。因为MergeTree 最开始支撑的应用场景是基于用户行为的数据分析,这些从客户端埋点采集回来的时序数据数据是不需要考虑修改或者删除的。但是随着应用案例的进一步的扩大,历史数据的更新会是一个如何都绕不开的需求,特别是在一些出具月报、年报这类分析报表的场景,底层历史数据会发生变化是家常便饭的事情。
MergeTree 面临的第三个挑战不能说是它的一个问题,而可以看做是用户对它提出了更高的期望,你还能再快一些吗?MergeTree 最擅长的主流使用场景是建立一张拥有数百个字段的大宽表,然后直接面对这张明细宽表查询。假设一张用于保存用户行为数据的明细表有100亿行数据,而业务查询的需求是查询浏览次数最多的前10个网址。你看虽然有100亿行的原始数据,但业务上每次只需要查询前10行,面对这种查询条件非常明确且固化的场景,每次都从明细表直接查询就有点浪费资源了。这种浪费首先是存储的浪费,在极端情况下可能会浪费数千倍之多。因为终端用户只关心结果数据,所以没有必要一直保存所有的明细数据;其次是查询性能的浪费,虽然MergeTree实时查询的性能足够强大,但是每次查询都从明细数据聚合计算也占用了内存、CPU和磁盘IO资源。
未完待续。。。
原创不易,如果这篇文章对你有帮助,欢迎 点赞、转发、在看 三连击
本文分享自 ClickHouse的秘密基地 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!