LSMT存储引擎浅析| 青训营笔记

137 阅读11分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 32 天

本节课将介绍:

一般数据库会分为存储与计算两层,存储层在单节点层面会将数据交给存储引擎管理,存储引擎在其中承载了很多职责,重点讲述存储引擎的职责和 LSMT 相关背景知识。

在对 LSMT 和存储引擎讲解完成之后,会围绕着存储引擎流行的原因展开,讲述了它的优势,并对其实现进行了具体的剖析,同时以 RocksDB 为例,讲述了一款成熟的存储引擎是如何发挥硬件潜力的。

随着技术的不断演进,LSMT 已经积累了诸多优秀的实践案例,与此同时,基于 LSMT 的理论模型逐渐受到业界及学界的关注,LSMT 的理论模型也在逐步被完善,针对 LSMT 理论模型进行讲解,分析在模型完善背景下 LSMT 的实践与调优。

目前技术背景下,LSMT 存储引擎在大数据社区备受青睐,被广泛地应用于不同的场景之中,在公司内部也有一些有关 LSMT 存储引擎的应用实践,结合实际的工作案例讲解,带来 LSMT 最佳实践。

LSMT与存储引擎介绍

历史

  • LSMT是Log-Structured Merge-Tree的缩写,由Patrick 0 'Neil etc. 在1996年的论文,The Log-Structured Merge-Tree (LSM-Tree) ,提出。
  • 相较而言,B-Tree出现就早得多了,在1970年由Bayer, R.; McCreight, E.提出。
  • 早期的数据库系统一般都采用B-Tree 家族作为索引,例如MySQL。2000 年后诞生的数据库大多采用LSMT索引,例如Google BigTable ,HBase,Canssandra等。

LSMT是什么

image-20230219104243650

  • 一言以蔽之,通过Append-only Write + 择机 Compact 来维护结构的索引树。

存储引擎是什么

image-20230219104716432

  • 以单机数据库MySQL为例,大致可以分为:

    • 计算层
    • 存储层(存储引擎层)
  • 计算层主要负责SQL解析/查询优化/计划执行。

  • 数据库著名的ACID特性,在MySQL中全部强依赖于存储引擎。

  • ACID是什么/存储引擎哪些组件保障了这些特性?

    • Atomicity
      • Write-Ahead Log(WAL) / Redo Log
    • Consistency(Correctness)
      • 依赖于数据库整体
    • Isolation
      • Snapshot / 2PL(Phase Lock) + MVCC
    • Durability
      • Flusher遵循Sync语意
  • 除了保障ACID以外,存储引擎还要负责:

    • 屏蔽IO细节提供更好的抽象
    • 提供统计信息与Predicate Push Down能力
  • 存储引擎不掌控IO细节,让操作系统接管,例如使用mmap,会有如下问题:

    • 落盘时机不确定造成的事务不安全
    • IO Stall
    • 错误处理繁琐
    • Å无法完全发挥硬件性能

如果上面这些接口,由操作系统来抽象,而不是由存储引擎来抽象,会有很大的问题的昂!!!

LSMT存储引擎的优势和实现

LSMT和B+Tree的异同

image-20230219105728658

  • B+Tree中,数据插入是原地更新的
  • B+Tree在发生不平衡 or 节点容量达到阈值时,立即进行分裂来平衡

image-20230219105825056

  • LSMT与B+ Tree可以用统一模型描述

  • 从高层次的数据结构角度来看二者没有本质的不同,可以互相转化

  • 工程实践上还是用LSMT来表示一个Append-only和Lazy Compact的索引树,B+Tree来表示一个 Inplace-Update 和 Instant Compact 的索引树.

  • Append-only 和 Lazy Compact 这两个特性更加符合现代计算机设备的特性。

Why LSMT

  • AlI problems in computer science can be solved by another level of indirection — From Butler Lampson
  • 在计算机存储乃至整个工程界都在利用Indirection处理资源的不对称性
  • 存储引擎面对的资源不对称性在不同时期是不同的

HDD时代

HDD时代:顺序和随机操作性能不对称

image-20230219110244360

  • 由于机械硬盘需要磁盘旋转和机械臂移动来进行读写,顺序写吞吐是随机读的25倍。

SSD时代

HDD时代:顺序写和随机写性能不对称

image-20230219110347558

  • 由于SSD随机写会给主控带来GC压力,顺序写吞吐是随机写的6倍。

对比

  • 小结:
    • HDD时代,顺序操作远快于随机操作
    • SSD时代,顺序写操作远快于随机写操作

这二者的共性是顺序写是一个对设备很友好的操作,LSMT符合这一点,而B+ Tree依赖原地更新, 导致随机写。

RocksDB中LSMT存储引擎的实现

  • RocksDB是一款十分流行的开源LSMT存储引擎,最早来自Facebook ( Meta ),应用于MyRocks,TiDB等数据库。
  • 在字节内部也有Abase,ByteKV,ByteNDB,Bytable等用户。
  • 因此接下来将会以RocksDB为例子介绍LSMT存储引擎的经典实现。

Write

写WAL

image-20230219110632460

  • RocksDB写入流程主要有两个优化,批量WAL写入(继承自LevelDB )与并发MemTable更新
  • RocksDB在真正执行修改之前会先将变更写入WAL,WAL写成功则写入成功。

image-20230219110807535

  • 多个写入者会选出一个Leader,由这个Leader来一次性写入WAL,避免小IO。
  • 不要求WAL强制落盘(Sync)时,批量提交亦有好处,Leader可以同时唤醒其余Writer,降低了系统线程调度开销。

image-20230219110845977

  • 没有批量提交的话,只能链式唤醒。
  • 链式唤醒加大前台延迟。

image-20230219110917442

写Memtable

image-20230219111039446

  • 写完WAL还要写MemTable
  • RocksDB在继承LevelDB的基础上又添加了并发MemTable的写入优化

全流程

image-20230219111150761

  • WAL一次性写入完成后,唤醒所有Writer并行写入MemTable
  • 由最后一个完成MemTable写入的Writer执行收尾工作

Snapshot & SuperVision

image-20230219111245875

  • RocksDB的数据由3部分组成,MemTable / ImmemTable / SST.持有这三部分数据并且提供快照功能的组件叫做SuperVersion.(类似于MVCC)

  • MemTable和SST的释放依赖于引用计数。对于读取来说,只要拿着SuperVersion ,从MemTable一级一级向下,就能查到记录。拿着SuperVersion不释放,等于是拿到了快照。

  • 如果所有读者都给SuperVersion 的计数加1 ,读完后再减1, 那么这个原子引用计数器就会成为热点。CPU在多核之间同步缓存是有开销的,核越多开销越大。

  • 为了让读操作更好的scale ,RocksDB做了一个优化是Thread Local SuperVersion Cache

image-20230219111504895

  • 没有Thread Local 缓存时,读取操作要频繁 Acquire 和 Release SuperVersion
  • CPU 缓存不友好

image-20230219111620307

  • 有Thread Local缓存,读取只需要检查一下SuperVersion并标记Thread Local缓存正在使用即可

  • CPU缓存友好

Get & BloomFilter

image-20230219111816677

  • RocksDB 的读取在大框架.上和B+ Tree 类似,就是层层向下。

  • 相对于B+Tree ,LSMT点查需要访问的数据块更多。为了加速点查,一般LSMT引擎都会在SST中嵌入BloomFilter.

[1, 10]表示这个索引块存储数据的区间在1 - 10之间。查询2,就是顺着标绿色的块往下。

Compact - Level

  • Compact在LSMT中是将Key区间有重叠或无效数据较多的SST进行合并,以此来加速读取,或者回收空间。Compact 策略可以分为两大类,Level 和Tier。下图是Level策略:

image-20230219111946581

  • Level策略直接来自于LevelDB,也是RocksDB的默认策略。每一个层不允许有SST的Key

Compact - Tier

image-20230219112110248

Tier策略允许LSMT每层有多个区间重合的SST

  • 本层数量到达上限后 or 本层大小到达阈值,一次选择多个SST向下推。
  • 类似于MergeSort!!!

  • 读放大的增加 -> 写放大的减小

LSMT模型理论分析

HBase

image-20230219112407852

  • RocksDB 是单机存储引擎,那么现在都说云原生,HBase比RocksDB就更「云」一些,SST直接存储于HDFS上。
  • 二者在理论存储模型上都是LSMT.

模型算法复杂度分析

  • T: sizeratio,每层LSMT比上一层大多少,L0大小为1,则L1大小为T,L2为T^2,以此类推
  • L: level num,LSMT层数B:每个最小的10单位能装载多少条记录
  • M: 每个BloomFilter有多少bits
  • N: 每个BloomFilter生成时用了多少条Key
  • S: 区间查询的记录数量

image-20230219112455052

Level

  • Write
    • 每条记录抵达最底层需要经过L次Compact ,每次Compact Ln的一个小SST和Ln+1的一个大SST.
    • 设小SST的大小为1,那么大SST的大小则为T,合并开销是1+T,换言之将1单位的Ln的SST推到Ln+1要耗费1+T的IO, 单次Compact写放大为T
    • 每条记录的写入成本为1/B次最小单位IO。

O(Write_Level)=L*T*1/B=T*L/B

  • Point Lookup
    • 对于每条Key,最多有L个重叠的区间。
    • 每个区间都有BloomFilter ,失效率为e^(-M/N),只有当BloomFilter 失效时才会访问下一层。
  • O(PointLookup_ Level) = L * e^(-M/N)
    • 注意,这里不乘1/B系数的原因是写入可以批量提交拉低成本,但是读取的时候必须对齐到最小读取单元尺寸。

Tier

  • Point Lookup
    • 对于每条Key,有L层。
    • 每层最多有T个重叠区间的SST,对于整个SST来说有T*L个可能命中的SST,乘上BloomFilter的失效率,e^(-M/N),可得结果。
  • O(PointLookup_ Tier) = L* T * e^(-M/N) = T * L * e^(-M/N)
    • 注意,这里不乘1/B系数的原因是写入可以批量提交拉低成本,但是读取的时候必须对齐到最小读取单元尺寸。

算法复杂度分析

  • Online Quiz
    • Short Range Scan 复杂度是如何推导的?
    • Space Amplification 复杂度是如何推导的?

image-20230219113653136

  • 总结,Tier策略降低了写放大,增加了读放大和空间放大,Level策略增加了写放大,降低了读和空间放大。

LSMT存储引擎调优案例与展望

TerarkDB

image-20230219125638649

  • TerarkDB aka LavaKV 是字节跳动内部基于RocksDB深度定制优化的自研LSMT存储引擎,其中完全自研的KV分离功能,上线后取得了巨大的收益。
  • KV分离受启发于论文WiscKey: Separating Keys from Values in SSD-conscious Storage,概括起来就是Value较长的记录的Value单独存储。

Value短的,Value长的分开存储

TerarkDB & Abase & ByteGraph

image-20230219125846376

TerarkDB & Flink

  • 在字节内部Flink 流处理状态存储场景实测
  • 收益结论:
    1. 平均CPU开销在3个作业.上降低了26%~39%
    2. 峰值CPU开销在广告作业.上有明显的收益,降低了67%
    3. 平均容量开销在3个作业.上降低了17%~31.2%
    4. 直播业务某集群容量不收缩,TerarkDB的schedule TTL GC彻底解决了该问题

KV分离对于大Value优化是立竿见影的,LSTM对于大Value场景,最好就是去实现一个自己的KV分离昂!!!

新硬件

  • 随着硬件的发展,软件设计也会随着发生改变。近年来,出现了许多新的存储技术,例如SMR HDD,Zoned SSD / OpenChannel SSD , PMem等。如何在这些新硬件上设计/改进存储引擎是一大研究热点。

    • e.g. MatrixKV: Reducing Write Stalls and Write Amplification in LSM-tree Based KV Stores withMatrix Container in NVM

      这篇论文中的设计将LO整个搬进了PMem,降低了写放大。

新模型

  • 经典LSMT模型是比较简单的,有时候不能应对所有工况,可以提出新的模型来解决问题。

    • e.g. WiscKey: Separating Keys from Values in SSD-conscious Storage

      通过额外增加一个Value Store来存储大Value的记录来降低总体写放大。

    • e.g. REMIX: Efficient Range Query for LSM-trees

      通过额外增加一种SST的类型来加速范围查询的速度

新参数/新工况

  • 已有的模型, 在新的或者现有工况下,参数设置的不合理,可以通过更精确的参数设置来提升整体性能。
  • e.g. The Log- Structured Merge-Bush & the Wacky Continuum 在最后一层使用Level Compaction,之.上使用Tier Compaction,通过在除了最后一层以外的 SST加大BloomFilter 的bits 数来规避Tier Compaction带来的点查劣化。

课程总结

  • 单机数据库的ACID特性依赖于存储引擎
  • LSMT存储引擎的顺序写特性更适合现代计算机体系结构
  • LSMT和B+ Tree可以用同一模型描述并互相转化
  • Level Compaction策略,降低了读放大和空间放大,增加了写放大
  • Tier Compaction策略,降低了写放大,增大了读放大和空间放大
  • 分布式KV存储,如HBase,背后的理论模型与单机存储引擎RocksDB -样都是LSMT

References

  1. ACID 定义
  2. CIDR19 论文