深入理解LSM-Tree_sorted run lsm-程序员宅基地

技术标签: 云存储技术  lsm  数据库  存储技术  


lsm-tree的背景、定义和适用场景本文不再详述。本文意在论述LSM的存储引擎、工业取舍以及发展现状。

基础概念

  1. 读放大(read amplification):一次读取操作需要访问磁盘的次数,主要由compaction策略引入。可以引入cache和bloom filter优化。
  2. 写放大(write amplification):一次写操作带来的数据落盘的次数。在LSM-Tree中可由某个key数据在磁盘上存储的数量这个指标体现,主要是由compaction策略引入的。
  3. 空间放大(space amplification):实际空间占用/理论数据量,LSM-Tree的架构导致空间放大必然存在
  4. 临时空间:compaction任务需要临时的空间来完成多个sstable的合并。
  5. run:借用wiki中的解释:
    • The on-disk data is organized into sorted runs of data. Each run contains data sorted by the index key. A run can be represented on disk as a single file, or alternatively as a collection of files with non-overlapping key ranges. To perform a query on a particular key to get its associated value, one must search in the Level 0 tree and each run.

    • 可见,每一个run都是有序的数据块。

compaction策略

LSM-Tree存储引擎的工作流程如下:

  1. 当写入时,将其天际到内存的平衡树数据结构中(如红黑树、AVL树或SkipList),这个内存树有时候被称为MemTable(leveldb)。
  2. 当内存表大于某个阈值(通常以MB为单位),将其作为SSTable文件写入磁盘,由于树已经维护了按键排序的key-value对,写磁盘的效率可以比较高效。新的SSTable文件称为数据库的最新部分,当SSTable写磁盘的同时,写入可以继续添加到一个新的内存表中
    1. 不同的实现使用了不同的压缩合并策略,用以优化write-intensive或read-intensive场景。
    2. LevelDB和RocksDB使用leveled compaction,HBase采用tiered compaction,Cassandra则同时支持这两种压缩。
  3. 为了处理读请求,首先尝试在内存表中查找key,然后按照LSM-Tree的层级依次向下查找,知道找到或未找到目标。
  4. 后台进程周期性的执行段合并与压缩过程,合并多个SSTable,丢弃已经stale的值。

从上面的存储流程可以看出,LSM-Tree的性能主要取决于Compaction策略,它主要分为两类,tiered compactionleveled compaction

  • tiered流派从Bigtable->HBase->Cassandra一脉相承,后面又有MyRocks、InfluxDB、Kudu等等
  • Leveled流派工程实现主要有LevelDB->RocksDB

Size-tired compaction strategy(STCS)/Tiered

tiered适合write-intensive workload,有较低的写放大,缺点是读放大和空间放大较高。

STCS

tiered compaction的策略是:memtable达到阈值之后刷入下一层(落盘)作为一个run,每层的run达到一定数量之后,将当前层所以的run进行compaction并刷入下一层,循环往复。这种策略给人直观的感受是,越下层的run越大,当然实际的策越要比描述的复杂,因为run与run之间重叠的部分是不确定的,有可能完全重叠,也有可能完全不重叠。

  • 由于所有的上层level的数据可以直接追加写入下层,所以写放大比较低
  • 由于每层都有多个run,且每个run中的数据可以重叠,所以点读操作需要由上到下遍历每一个run,读放大较严重。更不要提range查询肯定要合并每一层的每一个run的查询结果,读放大更严重。
  • 由于每层有多个run,每个run都可能含有重叠数据,所以空间放大较为严重。空间放大可以说是STCS最严重的问题了。Scylla的文章通过实验论证了这个问题
  • 每层的多个run需要先合并,才能写入下层,因此有可能导致临时空间较大。耗时也较长
  • 这里讨论一个特例time-series data。key是时间,几乎是恒定递增的,因此越是下层,run越是不重叠,写入和查询的策略都可以做对应的优化。

leveled compaction

leveled compaction和上面的STCS相比,降低了空间放大和读放大,比较适合read-intensive workload,当然这个也只是相对而言,如果真的是读很多,不如用B+ Tree。

leveled

leveled compaction每层只维护一个run,按照key排序可以分为多个partition(SSTable file)。每层容量达到一定限制后,选择某个sstable与上层merge。

  • 由于每层只有一个run,所以降低了读放大和空间放大。但是点查询仍然有可能会遍历所有的run、范围查询必然会遍历所以的run。
  • 由于leveled compaction上层与下层merge的过程有可能涉及到上下层所有的数据,可能造成上层run全量重写,导致写入放大,但是由于每个run可以分为多个partition(sstable),因此可以节约部分临时空间。
Leveled-N

不同于leveled,Leveled-N允许每层多个Sorted run。Dostoevsky paper引入了一种Fluid LSM:允许最高层只有一个run,而其它层可以有多个run。

Hybrid

这里讨论scylladb实现的Hybrid模式。主要体现在这篇文章,其希望推出一种模式,可以吸纳tiered和leveled两者的优点。scylladb引入Hybrid主要为了优化tiered糟糕的空间放大。我推测主要有以下几点优化:

  1. 总体布局和tiered类似,只不过每层的run的数量可以根据负载动态调节,下层的run可以直接merge到上层的run里面。
  2. 运行时可以感知到当前磁盘剩余空间和run的key分布,如果剩余空间不多或这个每个run重叠太多(说明是overwrite场景),则compaction略逐步由tiered向leveled转移。
  3. 总的来说我觉得这个优化是将rocksDB繁杂的优化参数调整为代码中的一些策略,以优化特定的workload

example

Time-Window

这里主要讲scylladb/Cassandra针对时序数据引入的Time-Window compaction,时序数据有以下特点:

  1. key是单调递增的(timeline),
  2. 写入速率基本恒定,
  3. 查询方式以range-query为主,如查询某年、月、日的数据
  4. 可以通过设置TTL的方式删除超过一定时间的数据。

这种数据状况下,通过将不同的时间分配在不同的Time-Window中,将SSTable分配成了一个个time bucket,实现形式上采用tiered compaction,不同的time bucket不会合并(优化读操作)。由于overlap并不多,所以compaction压力不大,空间放大也不大。由于数据分布较好,查询也较方便。

比较

scylladb的文章总结了以下表格:

workload Size-Tiered Leveled Hybrid Time-Window
Write-only 2x peak space 2x writes Best -
Overwrite(指同一个key多次update) Huge peak space write amplification high peak space bug not like size-tiered -
Read-mostly, few updates read amplification Best read amplification -
Read-mostly, but a lot of updates read and space amplification write amplification may overwhelm read amplification -
Time series write,read,and space amplification write and space amplification write and read amplification best

工业实现

leveldb

顾名思义,levelDB使用leveled compaction,其CRUD流程如下:

  1. 写流程:
    1. 找到合适的memtable(skiplist)并写入数据,如果memtable写完并且immutableTable还未完成持久化,或者L0文件太多,则等待。如果memtable满了,则换一个memtable,并触发compaction。由此可见,l0tiered,对于每个run,key的范围有重叠。其它层是leveled
    2. 写日志,下刷日志
    3. 写memtable
  2. 读流程:
    1. 首先查找memtable,再查找immutable memtable
    2. 如果没有,查找下面所有持久化的levelVersion::Get,逐层向下搜索。搜索方法的策略是并不是遍历每一个sstable,而是先看需要查找的key是否落在sstable的范围内,如果落在,对sstable搜索。
    3. 对于读取mem中的数据(skiplist),正向遍历时间复杂度是O(1),反向遍历时间复杂度是O(log(N))
    4. 对于读取sstable中的数据,正向遍历有指针指向下一个数据位置,反向遍历每次Prev()都要重新定位
  3. 删除流程:
  4. 后台线程也会按需做compaction。通过MaybeScheduleCompaction()函数判断。

levelDB代码清晰,代码量少,阅读方便,不过缺少大规模工程应用场景。

RocksDB

rocksdb

LSM-Tre工业级实现,和levelDB相比,参数更多,优化更多,使用更灵活,Features Not in LevelDB这篇文章介绍了RocksDB相对于levelDB所做的优化,我选择一些优点列举如下:

  1. 实现了tiered(Universal Compaction),tiered+leveled(l0 tiered,高层leveled),和FIFO compaction(一条数据写入后,超过 TTL 指定的时间戳,就过期淘汰,对用户不可见。)
  2. 前缀遍历,针对相同前缀(prefix)的场景做了优化Options.prefix_extractor
  3. checksum数据校验
  4. 支持多线程compaction
  5. 支持全量和备份
  6. 支持read-only模式以优化读速度
  7. memtable优化:
    1. 支持多种数据结构:skiplist为默认,vector模式以优化Bacth-insert,hashtable模式以优化读操作。
    2. 支持多memtable并发插入(Memtable Pipelining)
    3. memtable持久化的时候会做合并,GC掉某些旧数据
  8. Column Families,可以设置某个kv归并为一个column family列族,如{cf1, key1, value1},列族之间可以相互隔离(不共享memtable和sstable)
Write Stalls

RocksDB会在某种情况下限制前台写请求的速度,称为Write Stalls。产生的原因不外乎因为某种原因,compaction的速率赶不上前台write的速率了。这种现象如果持续下去,会产生以下结果:

  1. 空间放大增大,写入的数据来不及压缩,最终耗尽磁盘空间
  2. 读放大增大,越是来不及compact,读放大就越大

这种情况下需要将前台写请求降速,直到compaction执行到某种可以继续高效处理前台读写请求的程度。

一般情况下,Write Stall产生的原因有以下几种可能:

  1. memtable,前台写入太快,memtable过多,而且都没有flush进l0
  2. l0 SSTable过多,导致读放大、merge也慢
  3. pending compaction bytes过多。磁盘 I/O 能力在业务高峰跟不上写入

scyllaDB/cassandra

scyllaDB/cassandra默认采用tiered,可以配置为levled、Hybrid或Time-Window。只有Hybrid是ScyllaDB独有。实现细节如上所述

hbase

HBase是一个分布式,版本化,面向列的开源数据库(面向列族Column Family,在)、分布式持久化KV数据库。HDFS(仅支持追加写)为Hbase提供可靠的底层数据存储服务,MapReduce为Hbase提供高性能的计算能力,Zookeeper为Hbase提供稳定服务和Failover机制,因此我们说Hbase是一个通过大量廉价的机器解决海量数据的高速存储和读取的分布式数据库解决方案。Hbase适合存储PB级别的海量数据,虽然当个请求是时延不低,但是可以通过并行请求增大吞吐,并且单个IO的时延下降并不多。

架构和bigtable比较类似,通过key-range,将数据水平切分到多台服务器上,数据持久化在HDFS中。这点和现代的应用如tidb略有不同,其是将数据切分之后通过一致性协议存储在多个RSM上,每个RSM的数据引擎为LSM-Tree

Hbase 技术细节笔记(上)

hbase在实现中,是把整个内存在一定阈值后,flush到disk中,形成一个file,这个file的存储也就是一个小的B+树,因为hbase一般是部署在hdfs上,hdfs不支持对文件的update操作,所以hbase这么整体内存flush,而不是和磁盘中的小树merge update。内存flush到磁盘上的小树,定期也会合并成一个大树。整体上hbase就是用了lsm tree的思路。

因为小树先写到内存中,为了防止内存数据丢失,写内存的同时需要暂时持久化到磁盘,对应了HBase的HLog(WAL)和MemStore

MemStore上的树达到一定大小之后,需要flush到HRegion磁盘中(一般是Hadoop DataNode),这样MemStore就变成了DataNode上的磁盘文件StoreFile,定期HRegionServer对DataNode的数据做merge操作,彻底删除无效空间,多棵小树在这个时机合并成大树,来增强读性能。

TiKV/Titan

TiKV使用RocksDB作为存储引擎,并且实现了Titan作为RocksDB 的高性能单机 key-value 存储引擎插件。以支持大value(Blob),其核心特性在于支持将 value 从 LSM-tree 中分离出来单独存储,以降低写放大。

其主要设计灵感来源于 USENIX FAST 2016 上发表的一篇论文 WiscKey。WiscKey 提出了一种高度基于 SSD 优化的设计,利用 SSD 高效的随机读写性能,通过将 value 分离出 LSM-tree 的方法来达到降低写放大的目的。

Titan 适合在以下场景中使用:

  1. 前台写入量较大,RocksDB 大量触发 compaction 消耗大量 I/O 带宽或者 CPU 资源,造成 TiKV 前台读写性能较差。
  2. 前台写入量较大,由于 I/O 带宽瓶颈或 CPU 瓶颈的限制,RocksDB compaction 进度落后较多频繁造成 write stall。
  3. 前台写入量较大,RocksDB 大量触发 compaction 造成 I/O 写入量较大,影响 SSD 盘的寿命。

学术研究

Dostoevsky

这篇论文详细地分析了各种操作在tired和leveled不同策略下的i/o复杂度,使用了level num、相邻level size比、entry数量、buffer size等一系列相关变量推导出不同操作具体的I/O代价公式和空间放大,对于point read还考虑了bloom filter带来的影响,range query分了short和long两种来分析。并且提出了优化策略lazying leveling,是tiering和leveling的结合体,最大一层使用leveling其它层使用tiering,lazying适合包含update、point lookup和long range lookup的混合负载,tiering适合大多为update的负载,leveling适合大多为lookup的负载。通过控制merge频率在tiering、leveling和lazying leveling几种不同策略下转换以适应不同的workload,称作fluid LSM((类似rocksdb的leveled-n)),并提出Dostoevsky,在执行期间动态计算最优配置以到达最大吞吐。

从作者的分析中可以看出compaction这个机制的复杂程度,要使用一套通用机制在不同场景下消耗最少的资源带来最好的性能基本是不可能的,实际工业界实现中需要考虑更多的因素(cache、delete、任务拆分粒度、复用技术等),因此大部分系统使用多种策略多个参数用以适配不同场景。

参考链接

  1. Cassandra Compaction
  2. LevelDB 之 Compaction
  3. 深入探讨LSM Compaction机制
  4. LSM Tree的Leveling 和 Tiering Compaction
  5. Scylla’s Compaction Strategies Series: Space Amplification in tiered CompactionSTCS图出处
  6. Size Tiered Compaction Strategy In Apache Cassandra
  7. rocksdb Leveled Compaction 图文并茂
  8. RocksDB Overview
  9. RocksDB Performance Benchmarks
  10. Direct IO什么场景里可以使用directIO
  11. RocksDB调优指南翻译自官方wiki
  12. RocksDB中文网
  13. RocksDB Tuning Guide
  14. Compaction rocksdb讲tirered和leveled
  15. Name that compaction algorithmTiered/Leveled比较,这哥们专门做lsmtree的。
  16. How to Ruin Your Performance by Choosing the Wrong Compaction Strategyscylladb的这篇文章写得很好
  17. Scylla’s Compaction Strategies Series: Write Amplification in Leveled Compaction
  18. Scylla’s Compaction Strategies Series: Space Amplification in Size-Tiered Compaction
  19. Titan 的设计与实现
  20. SIGMOD’18|Dostoevsky
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zxpoiu/article/details/116190330

智能推荐

基于Kepler.gl 和 Google Earth Engine 卫星数据创建拉伸多边形地图-程序员宅基地

文章浏览阅读965次,点赞18次,收藏21次。现在我们有了 2021 年和 2023 年的 NDVI 数据帧,我们需要从 2021 年的值中减去 2023 年的值以捕获 NDVI 的差异。该数据集包括像素级别的植被值,我们将编写一个自定义函数来根据红色和绿色波段的表面反射率计算 NDVI。在我的上一篇文章中,我演示了如何将单个多边形分割/镶嵌为一组大小均匀的六边形。现在我们有了植被损失数据,让我们使用 Kepler.gl 可视化每个六边形的植被损失。将地图保存为 HTML 文件,在浏览器中打开 HTML 以获得更好的视图。现在我们将调用该函数并使用、

Echarts绘制任意数据的正态分布图_echarts正态分布图-程序员宅基地

文章浏览阅读3.3k次,点赞6次,收藏5次。正态分布,又称高斯分布或钟形曲线,是统计学中最为重要和常用的分布之一。_echarts正态分布图

Android中发送短信等普通方法_android bundle.get("pdus");-程序员宅基地

文章浏览阅读217次。首先要在Mainfest.xml中加入所需要的权限:[html] view plain copyprint?uses-permission android:name="android.permission.SEND_SMS"/> uses-permission android:name="android.permission.READ_SMS"/> _android bundle.get("pdus");

2021-07-26 WSL2 的安装和联网_wsl2 联网-程序员宅基地

文章浏览阅读2.6k次。0、说明最近在学习 Data Assimilation Research Testbed (DART) 相关内容,其软件是在 Unix/Linux 操作系统下编译和运行的 ,由于我的电脑是 Windows 10 的,DART 推荐可以使用 Windows Subsystem For Linux (WSL) 来创建一个 Windows 下的 Linux 子系统。以下的内容主要介绍如何安装 WSL2,以及 WSL2 的联网。1、如何在 Windows 10 下安装WSL具体的安装流程可以在 microso_wsl2 联网

DATABASE_LINK 数据库连接_添加 database link重复的数据库链接命-程序员宅基地

文章浏览阅读1k次。DB_LINK 介绍在本机数据库orcl上创建了一个prod_link的publicdblink(使用远程主机的scott用户连接),则用sqlplus连接到本机数据库,执行select * from scott.emp@prod_link即可以将远程数据库上的scott用户下的emp表中的数据获取到。也可以在本地建一个同义词来指向scott.emp@prod_link,这样取值就方便多了..._添加 database link重复的数据库链接命

云-腾讯云-实时音视频:实时音视频(TRTC)-程序员宅基地

文章浏览阅读3.1k次。ylbtech-云-腾讯云-实时音视频:实时音视频(TRTC)支持跨终端、全平台之间互通,从零开始快速搭建实时音视频通信平台1.返回顶部 1、腾讯实时音视频(Tencent Real-Time Communication,TRTC)拥有QQ十几年来在音视频技术上的积累,致力于帮助企业快速搭建低成本、高品质音视频通讯能力的完整解决方案。..._腾讯实时音视频 分享链接

随便推点

用c语言写个日历表_农历库c语言-程序员宅基地

文章浏览阅读534次,点赞10次,收藏8次。编写一个完整的日历表需要处理许多细节,包括公历和农历之间的转换、节气、闰年等。运行程序后,会输出指定年份的日历表。注意,这个程序只是一个简单的示例,还有很多可以改进和扩展的地方,例如添加节气、节日等。_农历库c语言

FL Studio21.1.1.3750中文破解百度网盘下载地址含Crack补丁_fl studio 21 注册机-程序员宅基地

文章浏览阅读1w次,点赞28次,收藏27次。FL Studio21.1.1.3750中文破解版是最优秀、最繁荣的数字音频工作站 (DAW) 之一,日新月异。它是一款录音机和编辑器,可让您不惜一切代价制作精美的音乐作品并保存精彩的活动画廊。为方便用户,FL Studio 21提供三种不同的版本——Fruity 版、Producer 版和签名版。所有这些版本都是独一无二的,同样具有竞争力。用户可以根据自己的需要选择其中任何一种。FL Studio21.1.1.3750中文版可以说是一站式综合音乐制作单位,可以让您录制、作曲、混音和编辑音乐。_fl studio 21 注册机

冯.诺伊曼体系结构的计算机工作原理是,冯 诺依曼型计算机的工作原理是什么...-程序员宅基地

文章浏览阅读1.3k次。冯诺依曼计算机工作原理冯 诺依曼计算机工作原理的核心是 和 程序控制世界上不同型号的计算机,就其工作原理而言,一般都是认为冯 诺依曼提出了什么原理冯 诺依曼原理中,计算机硬件系统由那五大部分组成的 急急急急急急急急急急急急急急急急急急急急急急冯诺依曼结构计算机工作原理的核心冯诺依曼结构和现代计算机结构模型 转载重学计算机组成原理 一 冯 诺依曼体系结构从冯.诺依曼的存储程序工作原理及计算机的组成来..._简述冯诺依曼计算机结构及工作原理

四国军棋引擎开发(2)简单的事件驱动模型下棋-程序员宅基地

文章浏览阅读559次。这次在随机乱下的基础上加上了一些简单的处理,如进营、炸棋、吃子等功能,在和敌方棋子产生碰撞之后会获取敌方棋子大小的一些信息,目前采用的是事件驱动模型,当下完一步棋界面返回结果后会判断是否触发了相关事件,有事件发生则处理相关事件,没有事件发生则仍然是随机下棋。1.事件驱动模型首先定义一个各种事件的枚举变量,目前的事件有工兵吃子,摸暗棋,进营,明确吃子,炸棋。定义如下:enum MoveE..._军棋引擎

STL与泛型编程-第一周笔记-Geekband-程序员宅基地

文章浏览阅读85次。1, 模板观念与函数模板简单模板: template< typename T > T Function( T a, T b) {… }类模板: template struct Object{……….}; 函数模板 template< class T> inline T Function( T a, T b){……} 不可以使用不同型别的..._geekband 讲义

vb.net正则表达式html,VB.Net常用的正则表达式(实例)-程序员宅基地

文章浏览阅读158次。"^\d+$"  //非负整数(正整数 + 0)"^[0-9]*[1-9][0-9]*$"  //正整数"^((-\d+)|(0+))$"  //非正整数(负整数 + 0)"^-[0-9]*[1-9][0-9]*$"  //负整数"^-?\d+$"    //整数"^\d+(\.\d+)?$"  //非负浮点数(正浮点数 + 0)"^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0..._vb.net 正则表达式 取html中的herf