0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

数据结构LSM tree核心实现讲解

数据分析与开发 来源:sowhat1412 作者:sowhat1412 2021-09-30 14:19 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

LSM tree (log-structured merge-tree) 是一种对频繁写操作非常友好的数据结构,同时兼顾了查询效率。LSM tree 是许多 key-value 型或日志型数据库所依赖的核心数据结构,例如 BigTable、HBase、Cassandra、LevelDB、SQLite、Scylla、RocksDB 等。

LSM tree 之所以有效是基于以下事实:磁盘或内存的连续读写性能远高于随机读写性能,有时候这种差距可以达到三个数量级之高。这种现象不仅对传统的机械硬盘成立,对 SSD 硬盘也同样成立。如下图:

LSM tree 在工作过程中尽可能避免随机读写,充分发挥了磁盘连续读写的性能优势。

SSTable

LSM tree 持久化到硬盘上之后的结构称为 Sorted Strings Table (SSTable)。顾名思义,SSTable 保存了排序后的数据(实际上是按照 key 排序的 key-value 对)。每个 SSTable 可以包含多个存储数据的文件,称为 segment,每个 segment 内部都是有序的,但不同 segment 之间没有顺序关系。一个 segment 一旦生成便不再修改(immutable)。一个 SSTable 的示例如下:

可以看到,每个 segment 内部的数据都是按照 key 排序的。下面我们来介绍每个 segment 是如何生成的。

写入数据

LSM tree 的所有写操作均为连续写,因此效率非常高。但由于外部数据是无序到来的,如果无脑连续写入到 segment,显然是不能保证顺序的。对此,LSM tree 会在内存中构造一个有序数据结构(称为 memtable),例如红黑树。每条新到达的数据都插入到该红黑树中,从而始终保持数据有序。当写入的数据量达到一定阈值时,将触发红黑树的 flush 操作,把所有排好序的数据一次性写入到硬盘中(该过程为连续写),生成一个新的 segment。而之后红黑树便从零开始下一轮积攒数据的过程。

读取/查询数据

如何从 SSTable 中查询一条特定的数据呢?一个最简单直接的办法是扫描所有的 segment,直到找到所查询的 key 为止。通常应该从最新的 segment 扫描,依次到最老的 segment,这是因为越是最近的数据越可能被用户查询,把最近的数据优先扫描能够提高平均查询速度。

当扫描某个特定的 segment 时,由于该 segment 内部的数据是有序的,因此可以使用二分查找的方式,在

O(logn) 的时间内得到查询结果。但对于二分查找来说,要么一次性把数据全部读入内存,要么在每次二分时都消耗一次磁盘 IO,当 segment 非常大时(这种情况在大数据场景下司空见惯),这两种情况的代价都非常高。一个简单的优化策略是,在内存中维护一个稀疏索引(sparse index),其结构如下图:

稀疏索引是指将有序数据切分成(固定大小的)块,仅对各个块开头的一条数据做索引。与之相对的是全量索引(dense index),即对全部数据编制索引,其中的任意一条数据发生增删均需要更新索引。两者相比,全量索引的查询效率更高,达到了理论极限值

O(logn),但写入和删除效率更低,因为每次数据增删时均需要因为更新索引而消耗一次 IO 操作。通常的关系型数据库,例如 MySQL 等,其内部采用 B tree 作为索引结构,这便是一种全量索引。

有了稀疏索引之后,可以先在索引表中使用二分查找快速定位某个 key 位于哪一小块数据中,然后仅从磁盘中读取这一块数据即可获得最终查询结果,此时加载的数据量仅仅是整个 segment 的一小部分,因此 IO 代价较小。以上图为例,假设我们要查询 dollar 所对应的 value。首先在稀疏索引表中进行二分查找,定位到 dollar 应该位于 dog 和 downgrade 之间,对应的 offset 为 17208~19504。之后去磁盘中读取该范围内的全部数据,然后再次进行二分查找即可找到结果,或确定结果不存在。

稀疏索引极大地提高了查询性能,然而有一种极端情况却会造成查询性能骤降:当要查询的结果在 SSTable 中不存在时,我们将不得不依次扫描完所有的 segment,这是最差的一种情况。有一种称为**布隆过滤器(bloom filter)**的数据结构天然适合解决该问题。布隆过滤器是一种空间效率极高的算法,能够快速地检测一条数据是否在数据集中存在。我们只需要在写入每条数据之前先在布隆过滤器中登记一下,在查询时即可断定某条数据是否缺失。

布隆过滤器的内部依赖于哈希算法,当检测某一条数据是否见过时,有一定概率出现假阳性(False Positive),但一定不会出现假阴性(False Negative)。也就是说,当布隆过滤器认为一条数据出现过,那么该条数据很可能出现过;但如果布隆过滤器认为一条数据没出现过,那么该条数据一定没出现过。这种特性刚好与此处的需求相契合,即检验某条数据是否缺失。

文件合并(Compaction)

随着数据的不断积累,SSTable 将会产生越来越多的 segment,导致查询时扫描文件的 IO 次数增多,效率降低,因此需要有一种机制来控制 segment 的数量。对此,LSM tree 会定期执行文件合并(compaction)操作,将多个 segment 合并成一个较大的 segment,随后将旧的 segment 清理掉。由于每个 segment 内部的数据都是有序的,合并过程类似于归并排序,效率很高,只需要

O(n)O(n)的时间复杂度。

在上图的示例中,segment 1 和 2 中都存在 key 为 dog 的数据,这时应该以最新的 segment 为准,因此合并后的值取 84 而不是 52,这实现了类似于字典/HashMap 中“覆盖写”的语义。

删除数据

现在你已经了解了 LSM tree 读写数据的方式,那么如何删除数据呢?如果是在内存中,删除某块数据通常是将它的引用指向 NULL,那么这块内存就会被回收。但现在的情况是,数据已经存储在硬盘中,要从一个 segment 文件中间抹除一段数据必须要覆写其之后的所有内容,这个成本非常高。LSM tree 所采用的做法是设计一个特殊的标志位,称为 tombstone(墓碑),删除一条数据就是把它的 value 置为墓碑,如下图所示:

这个例子展示了删除 segment 2 中的 dog 之后的效果。注意,此时 segment 1 中仍然保留着 dog 的旧数据,如果我们查询 dog,那么应该返回空,而不是 52。因此,删除操作的本质是覆盖写,而不是清除一条数据,这一点初看起来不太符合常识。墓碑会在 compact 操作中被清理掉,于是置为墓碑的数据在新的 segment 中将不复存在。

LSM tree 与 B tree 的对比

主流的关系型数据库均以 B/B+ tree 作为其构建索引的数据结构,这是因为 B tree 提供了理论上最高的查询效率 O(log n)

O(logn)。但对查询性能的追求也造成了 B tree 的相应缺点,即每次插入或删除一条数据时,均需要更新索引,从而造成一次磁盘 IO。这种特性决定了 B tree 只适用于频繁读、较少写的场景。如果在频繁写的场景下,将造成大量的磁盘 IO,从而导致性能骤降。这种应用场景在传统的关系型数据库中比较常见。

而 LSM tree 则避免了频繁写场景下的磁盘 IO 开销,尽管其查询效率无法达到理想的 O(log n)

O(logn),但依然非常快,可以接受。所以从本质上来说,LSM tree 相当于牺牲了一部分查询性能,换取了可观的写入性能。这对于 key-value 型或日志型数据库是非常重要的。

总结

LSM tree 存储引擎的工作原理包含以下几个要点:

写数据时,首先将数据缓存到内存中的一个有序树结构中(称为 memtable)。同时触发相关结构的更新,例如布隆过滤器、稀疏索引。

当 memtable 积累到足够大时,会一次性写入磁盘中,生成一个内部有序的 segment 文件。该过程为连续写,因此效率极高。

进行查询时,首先检查布隆过滤器。如果布隆过滤器报告数据不存在,则直接返回不存在。否则,按照从新到老的顺序依次查询每个 segment。

在查询每个 segment 时,首先使用二分搜索检索对应的稀疏索引,找到数据所在的 offset 范围。然后读取磁盘上该范围内的数据,再次进行二分查找并获得结果。

对于大量的 segment 文件,定期在后台执行 compaction 操作,将多个文件合并为更大的文件,以保证查询效率不衰减。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    7314

    浏览量

    93938
  • SSD
    SSD
    +关注

    关注

    21

    文章

    3061

    浏览量

    121749
  • 过滤器
    +关注

    关注

    1

    文章

    442

    浏览量

    20830

原文标题:一种对频繁写操作非常友好的数据结构(核心实现讲解)

文章出处:【微信号:DBDevs,微信公众号:数据分析与开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    `lv_obj_tree.h` 在 **LVGL v9** 中的位置和作用

    )已经封装了这些逻辑,无需直接包含 lv_obj_tree.h。 总结 lv_obj_tree.h 是 LVGL 内部管理对象树结构核心头文件,位于 src/core/ 目录,主要供
    发表于 11-13 15:49

    LSM6DSV16BX:集成机器学习核心与Qvar感应的先进惯性测量单元

    加速度、角速度和Qvar检测数据。它具有专用配置、处理和滤波功能。LSM6DSV16BX在紧凑型封装 (2.5mm x 3.0mm x 0.71mm) 中集成了UI传感器、音频加速度计和Qvar传感器。
    的头像 发表于 10-28 13:45 241次阅读
    <b class='flag-5'>LSM</b>6DSV16BX:集成机器学习<b class='flag-5'>核心</b>与Qvar感应的先进惯性测量单元

    LSM6DSV16X:集成机器学习与静电感应的高性能惯性测量单元

    STMicroelectronics LSM6DSV16X iNEMO惯性模块是一款三轴数字加速度计和三轴数字陀螺仪。 LSM6DSV16X具有三个内核,用于处理三个独立通道上的加速度和角速率数据
    的头像 发表于 10-28 11:52 318次阅读
    <b class='flag-5'>LSM</b>6DSV16X:集成机器学习与静电感应的高性能惯性测量单元

    LSM6DSV iNEMO™惯性模块:为下一代智能设备提供高性能运动感知

    的配置、滤波和处理功能。LSM6DSV在高性能模式下提升性能(0.65mA时),具有不间断低功耗特性,实现最佳的消费者运动体验。该器件嵌入了高级专用功能,例如有限状态机和数据滤波,用于OIS、EIS和运动处理。
    的头像 发表于 10-28 11:25 317次阅读
    <b class='flag-5'>LSM</b>6DSV iNEMO™惯性模块:为下一代智能设备提供高性能运动感知

    基于LSM6DSO16IS适配板数据手册的技术解析与应用指南

    STMicroelectronics STEVAL-MKI229A适配器板用于评估LSM6DSO16IS MEMS器件。该板提供了有效、快速的系统原型设计解决方案,可直接在用户的’应用程序内评估。
    的头像 发表于 10-27 11:03 275次阅读
    基于<b class='flag-5'>LSM</b>6DSO16IS适配板<b class='flag-5'>数据</b>手册的技术解析与应用指南

    LSM6DSV16B:面向TWS与可穿戴设备的6轴IMU,融合传感与低功耗智能

    于提供精确的加速度和角速度感测数据处理。 LSM6DSV16B具有专用于运动跟踪、设备电源管理、游戏3D头部跟踪和增强音频体验的双通道,提供无与伦比的多用性和功能。该器件还具有单独通道,用于通过专用配置、处理和滤波实现振动检测
    的头像 发表于 10-25 14:28 811次阅读
    <b class='flag-5'>LSM</b>6DSV16B:面向TWS与可穿戴设备的6轴IMU,融合传感与低功耗智能

    LSM6DSV320X:面向高端应用的智能6轴惯性测量单元

    。STMicroelectronics IMU设计用于提供精确的运动检测和传感器融合功能,因此非常适合用于汽车碰撞检测、运动监控和物联网 (IoT) 设备。LSM6DSV320X采用四通道架构,可在四个独立通道上处理加速度和角速度数据
    的头像 发表于 10-21 14:34 289次阅读
    <b class='flag-5'>LSM</b>6DSV320X:面向高端应用的智能6轴惯性测量单元

    LSM6DSV80X 6轴惯性测量单元技术解析

    STMicroelectronics LSM6DSV80X 6轴惯性测量单元 (IMU) 在单个元件中集成了两个加速度计结构,可实现16g和80g的全量程传感、嵌入式智能以及高达4000dps
    的头像 发表于 10-15 17:42 537次阅读
    <b class='flag-5'>LSM</b>6DSV80X 6轴惯性测量单元技术解析

    分享一个嵌入式学习阶段规划

    给大家分享一个嵌入式学习阶段规划: (一)基础筑牢阶段(约 23 天) 核心目标:打牢 C 语言、数据结构、电路基础C 语言开发:学变量 / 指针 / 结构体等核心语法,用 Dev-
    发表于 09-12 15:11

    【HZ-T536开发板免费体验】6、使用protoc-gen-gorm生成标准化的数据结构

    在设计espnow协议的时候,考虑到我需要在esp32,Linux设备,web上使用相同的数据结构,那就需要考虑一下,是否使用一个通用的跨平台序列化数据结构。这时候我想起了protobuf,这个就是
    发表于 08-26 00:32

    程序设计与数据结构

    的地址)出发,采用推导的方式,深入浅出的分析了广大C程序员学习和开发中遇到的难点。 2. 从方法论的高度对C语言在数据结构和算法方面的应用进行了深入讲解和阐述。 3. 讲解了绝大多数C程序员开发
    发表于 05-13 16:45

    RK3588 EVB开发板原理图讲解【八】 RK3588 power Tree

    本帖最后由 瑞芯微方案开发老王 于 2025-3-1 11:41 编辑 一、RK3588电源架构核心特点 ​多电源域设计​ 芯片通常划分为多个独立电源域(Power Domain),例如
    发表于 03-01 11:38

    结构数据中台:企业AI应用安全落地的核心引擎

    在数字化转型浪潮中,非结构数据(如文档、图片、音视频等)已成为企业核心资产,其价值挖掘能力直接影响AI应用的效能与安全性。然而,数据分散、多模态处理复杂、安全合规风险高等问题,严重制
    的头像 发表于 02-27 17:06 838次阅读

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-初识设备树之设备树组成和结构

    的一项技能。设备树的起源设备树(Device Tree)是一种描述硬件资源的数据结构,它由uboot传递给Linux内核,被内核解析,内核根据设备树中的硬件描述信息加载利用相应驱动资源。在引入设备树
    发表于 01-08 08:32

    飞凌嵌入式ElfBoard ELF 1板卡-初识设备树之设备树组成和结构

    的一项技能。设备树的起源设备树(Device Tree)是一种描述硬件资源的数据结构,它由uboot传递给Linux内核,被内核解析,内核根据设备树中的硬件描述信息加载利用相应驱动资源。在引入设备树
    发表于 01-07 09:16