Clickhouse 系列 - 番外 - LSM 算法

ByteRiftMaster
• 阅读 2722

在本系列的第三章中介绍了 clickhouse 通过 block 和 lsm 来减少磁盘读取的数据量。严谨的逻辑应该时 clickhouse 通过 lsm 算法来实现数据预排序,从而减少了磁盘读取的数据量,本章番外主要为读者介绍什么是 LSM 算法,对 LSM 算法已经有了解的读者可以跳过本章。

LSM 算法最早出现在 1991 年的 ACM 期刊上,之后其思想在各大大数据存储系统中被广泛使用,例如 LevelDB,HBase,Cassandra……LSM 算法由于适应的场景不同,存在很多的变体,clickhouse 也使用 lsm 算来实现其预排序的功能,本文将着重介绍 clickhouse 中的使用,同时也会适当涉及一些其他系统的使用用以让读者体会架构设计的随心所欲。

我们都知道,用户在调用 insert 向 clickhouse 插入数据时,数据不一定是按已经按照排序键排序好的数据,大概率是乱序数据。那么这种乱序的请求如何做到写入磁盘时变得有序呢?这个就是 LSM 算法实现的。

LSM 算法的几个核心步骤:

  • 在于数据写入存储系统前首先记录日志,防止系统崩溃
  • 记录完日志后在内存中以供使用,当内存达到极限后写入磁盘,记录合并次数 Level 为 0(L=0)。已经写入磁盘的文件不可变。
  • 每过一段时间将磁盘上 L 和 L+1 的文件合并

我们用一个示例来展示下整个过程

T=0 时刻,数据库为空。

T=1 时刻,clickhouse 收到一条 500 条 insert 的插入请求,这 500 条数据时乱序的。此时,clickhouse 开始插入操作。首先将 500 条插入请求一次性写入日志。接着在内存中进行排序,排序完成后将有序的结果写入磁盘,此时 L=0;

T=2 时刻,clickhouse 收到一条 800 条 insert 的插入请求,这 800 条数据时乱序的。此时,clickhouse 开始插入操作。首先将 800 条插入请求一次性写入日志。接着在内存中进行排序,排序完成后将有序的结果写入磁盘,此时 L=0;

T=3 时刻,clickhouse 开始合并,此时此刻,磁盘上存在两个 L=0 的文件。这两个文件每个文件内部有序,但可能存在重合。(例如第一批 500 条的范围是 300-400,第二批 800 条数据的范围是 350-700)。因此需要合并。clickhouse 在后台完成合并后,产生了一个新的 L=1 的文件。将两个 L=0 的文件标记为删除。

T=4 时刻,clickhouse 开始清理,将两个被标记为删除的文件真正地物理删除。

T=5 时刻,clickhouse 收到一条 100 条 insert 的插入请求,这 100 条数据时乱序的。此时,clickhouse 开始插入操作。首先将 100 条插入请求一次性写入日志。接着在内存中进行排序,排序完成后将有序的结果写入磁盘,此时 L=0;

T=6 时刻,clickhouse 开始合并,此时此刻,磁盘上存在 1 个 L=0 的文件和 1 个 L=1 的文件。这两个文件每个文件内部有序,但不存在重合。(例如 L0 文件的范围是 100-200,L1 文件的范围是 300-700)。因此不需要合并。clickhouse 在后台将 L=0 的文件升级成 L=1,此时数据库内存在两个 L=1 的互不重合的文件。

……

以上就是 LSM 算法在 clickhouse 上的应用,我们总结一下,clickhouse 使用 LSM 算法将乱序的数据在内存中排序为有序的数据,然后写入磁盘保存,并且定期合并有重合的磁盘文件。

不难发现,上述所有的过程对于磁盘的来说都是顺序写,因此这个也是 LSM 算法的一个特点——可以将大量的随机写入转换为顺序写入从而减少磁盘 IO 时间。leveldb 就借助了 lsm 的这个特性。当然,clickhouse 并没有使用到这个特性。下面会将简单介绍下 leveldb 是如何使用 LSM 的。

clickhouse 借助 LSM 实现了预排序的功能,提高了磁盘的利用率,但也同时带来了一些牺牲。再次强调,没有完美的架构,当架构解决一个问题的同时,一定会带来一个全新的问题。

对于 clickhouse 也一样,读者们已经知道了,clickhouse 会在多次 insert 请求时创建独立的数据文件。虽然 clickhouse 会在合适时间进行合并,但如果查询发生在合并前,就有可能数据分布在两个数据文件内。此时 clickhouse 默认会返回两个列表,这两个列表内部有序,但相互之间却会有重合。这就给用户使用带来了不便,下图展示了这种情况。

Clickhouse 系列 - 番外 - LSM 算法

可以看出,此时 clickhouse 未合并时查询结果分成了 4 个独立的结果,每个结果内部有序,但相互之间存在重合,也就说对于这种情况需要用户自行合并。我们等待其合并后再次查询,结果如下:

Clickhouse 系列 - 番外 - LSM 算法

clickhouse 合并后就能解决该问题。

LevelDB 的用法

leveldb 是一个允许修改的数据库,因此其对于 LSM 的使用和 clickhouse 类似,主要的不同在于写入日志后的操作不同。

clickhouse 在记录日志后,会直接在内存中进行排序,从而写入磁盘。此时如果 clickhouse 又接到一条写入情况,会重新开启一个新的进程。

而 leveldb 在记录日志后,会将数据首先缓存在内存中,等待后续操作继续操作这块内存,直到这块内存被填满,才会一次性将数据写入磁盘。

这个差异主要时两个数据库面向的场景不同,clickhouse 主要面向读多写少的分析场景,强调大批量一次性写入增加吞吐量。而 leveldb 主要面向写多读少的业务场景,强调低延时。

吞吐量和延时一向是互相对立的两个指标,不同系统都在这两个指标之间存在取舍。后续有机会我也会写一篇关于这两个指标之间的相爱相杀,以及知名开源软件在这两个指标之间的思考。

感想

扯回来,正因为面向的场景不同,clickhouse 和 leveldb 对 LSM 的使用存在着不同。这也给了我们一个启发,作为架构师,我们要做到运用之妙存乎一心。要能够了解我们正在设计的业务的需求是什么,然后进行符合需求的修改。而不是无脑地认为 LSM 一定是用在写多读少的场景。

做到这一点会有点难,但幸好我们可以站在前人肩膀上,多体会一下前人设计的精妙绝伦的架构。有了这样的经验和思考,我们在遇到相同问题的时候就能做到更深的思考。

这也是我写这个系列的原因,clickhouse 真的是工程师设计的典范之作,整个 clickhouse 没有发明新的科学理论,但却让我们看到了借助已有的理论也能将性能在某一方面发挥到极致,这种追求极致的工程师精神让我深深着迷,我觉得我需要将这种精妙的设计思想的传递给大家。希望有朝一日,我们中国的工程师也能将极致的产品带给世界。因为有你,因为有我,许许多多平凡而伟大的工程师的共同努力,这一天一定能够到来。向 clickhouse 的研发团队致敬。

点赞
收藏
评论区
推荐文章
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
从历代GC算法角度刨析ZGC
本文所有介绍仅限于HotSpot虚拟机,本文先介绍了垃圾回收的必要手段,基于这些手段讲解了历代垃圾回收算法是如何工作的,每一种算法不会讲的特别详细,只为读者从算法角度理解工作原理,从而引出ZGC,方便读者循序渐进地了解。
TiDB 底层存储结构 LSM 树原理介绍
随着数据量的增大,传统关系型数据库越来越不能满足对于海量数据存储的需求。对于分布式关系型数据库,我们了解其底层存储结构是非常重要的。本文将介绍下分布式关系型数据库TiDB所采用的底层存储结构LSM树的原理。
Stella981 Stella981
3年前
RocksDB Java操作
RocksDB其实是一种嵌入式的K:V数据库,系统无需安装,之前本人的安装RocksDB安装(https://my.oschina.net/u/3768341/blog/4928501),其实多此一举。由于RocksDB是C开发的,它的JavaAPI大多其实只是对CAPI的一种调用。RocksDB的底层数据结构是一种LSM树,可以参考
Stella981 Stella981
3年前
Sorry!Hbase的LSM Tree就是可以为所欲为!
我们先抛出一个问题:!file(https://oscimg.oschina.net/oscnet/upd5d01172c006977f680f3d99ad039ce7279.png)LSM树是HBase里使用的非常有创意的一种数据结构。在有代表性的关系型数据库如MySQL、SQLServer、Oracle中,数据存储与索引的基本结构就是我们
Stella981 Stella981
3年前
Hash冲突和一致性
对于hash算法,有几个问题避无可避的。问题1:hash冲突的问题?1\.背景介绍:在数据量很大的时候,就会出现hash之后的数值,指向相同的位置,也就是所谓的hash冲突。这个取决于hash算法的好坏,以及数据量的大小,hash算法越差,数据量越大,hash冲突的概率就会越大。2\.然而一旦出现了hash冲突,我们该怎么办
Stella981 Stella981
3年前
Log Structured Merge Trees(LSM) 算法
十年前,谷歌发表了“BigTable”的论文,论文中很多很酷的方面之一就是它所使用的文件组织方式,这个方法更一般的名字叫LogStructuredMergeTree。LSM是当前被用在许多产品的文件结构策略:HBase,Cassandra,LevelDB,SQLite,甚至在mangodb3.0中也带了一个可选的LSM引擎(Wired
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
搜索中常见数据结构与算法探究(二)
本文介绍了几个常见的匹配算法,通过算法过程和算法分析介绍了各个算法的优缺点和使用场景,并为后续的搜索文章做个铺垫;读者可以通过比较几种算法的差异,进一步了解匹配算法演进过程以及解决问题的场景;KMP算法和DoubleArrayTireTree是其中
分布式系统的主键生成方案对比 | 京东云技术团队
UUID​UUID(通用唯一识别码)是由32个十六进制数组成的无序字符串,通过一定的算法计算出来。为了保证其唯一性,UUID规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(