本文是阅读了DDIA的第三章后整理的读书笔记,总结了什么是LSM存储引擎,以及在实现上的一些细节优化。

1 什么是LSM?

LSM一词最早来自于Partrick O'Neil et al.发表的文章[1],全称为"Log-Structured Merged-Tree"。后来,Google发表的Bigtable论文[2]将其发扬光大。

目前所有基于该思想实现的存储引擎,我们都可以称之为“LSM存储引擎”,例如:

  • LevelDB。Google开源的单机版Bigtable实现,使用C++编写。
  • RocksDB。来自Facebook,基于LevelDB改良而来,使用C++编写。
  • HBase。最接近的BigTable开源实现,使用Java编写,是Hadoop的重要组成部分。
  • Cassandra。来自Facebook的分布式数据库系统,同样借鉴了BigTable设计思想,使用Java编写。
  • 其它等等。

不同于传统的基于B+树的数据库存储引擎,基于LSM的引擎尤其适合于写多读少的场景。

我们先从一个最简单的存储引擎示例出发,然后再描述LSM引擎的基本原理。

2 最基本的存储引擎

一个最基本的存储引擎,需要支持下面两个操作:

  • Put(Key, Value)
  • Get(Key)

加快写操作

我们知道,磁盘特别是机械硬盘,其随机写的速度是非常惨不忍睹的。但是,如果我们是顺序写磁盘的话,那速度跟写内存是相当的:因为减少了寻道时间和旋转时间。而且顺序写的情况下,还能将数据先放到buffer,等待数量达到磁盘的一页时,再落盘,能进一步减少磁盘IO次数。

所以,这里我们规定,每一次写数据都追加到数据文件的末尾。

#!/bin/sh
# usage: ./Put key value
echo "$1:$2" >> simpledb.data
复制代码

注:如果对同一个key写多次,最终以最后一次的值为准(即对于读请求,应返回最后一次写入的值)。

加快读操作

要从数据文件里面查询:

# usage: ./Get key
grep "^$1:" simpledb.data | sed -e 's/^://g' | tail -n 1
复制代码

直接从数据文件查询的效率是很低的:我们需要遍历整个数据文件。

这时候就需要“index”来加快读操作了。我们可以在内存中保存一个key到文件偏移量的映射关系(哈希表索引):在查找时直接根据哈希表得到偏移量,再去读文件即可。

当然,加了索引表也相应地增加了写操作的复杂度:写数据时,在追加写数据文件的同时,也要更新索引表。

这样的建索引的方式有个缺点:因为索引map必须常驻内存,所以它没法处理数据量很大的情况。当内存无法加载完整索引数据时,就无法工作了。

防止数据文件无止尽的增长

我们再来看看另外一个问题:传统的B+树,每个key只会存一份值,占用的磁盘空间是跟数据量严格对应的。但是在追加写的方案中,磁盘空间是永无止尽的,只要这个系统在线上运行,产生写请求,文件体积就会增加。

解决文件无限增长的方法就是 compaction

  • 将数据文件分段(segment)。每个segment文件最大不超过多少字节,当segment满时创建一个新的segment。当前写入的segment称为活跃segment。同一时刻只有一个活跃segment。
  • 后台定期将旧的segment文件合并压缩:每个key只保留最新的值。

以上就是一个简单的基于内存索引+文件分段并定期压缩的存储引擎。可以看到,它能够提供很好的写入性能,但是无法应对数据量过大的场景。

3 LSM存储引擎

首先看看怎么解决内存索引过大的问题。

假定现在我们要存储Nkey-value,那么我们同样需要在索引里面保存Nkey-offset。但是,如果数据文件本身是按序存放的,我们就没必要对每个key建索引了。我们可以将key划分成若干个block,只索引每个blockstart_key。对于其它key,根据大小关系找到它存在的block,然后在block内部做顺序搜索即可。

在LSM里面,我们把按序组织的数据文件称为SSTableSorted String Table)。只保存block起始key的offset的索引,我们称为“稀疏索引”(Sparse Index)。

而且,有了block的概念之后,我们可以以block为单位将数据进行压缩,以达到减少磁盘IO吞吐量。

那怎么生成和维护SSTable呢?

我们可以在内存里面维护一个平衡二叉树(例如AVL树或者红黑树)。每当有Put(Key, Value)请求时,先将数据写入二叉树,保证其顺序性。当二叉树达到既定规模时,我们将其按序写入到磁盘,转换成SSTable存储下来。

在LSM里面,我们把内存里的二叉树称为memtable

注意,这里的memtable虽然也是存在内存中的,但是它跟上面说的稀释索引不一样。对每一个SSTable,我们都会为它维护一个稀疏的内存索引;但是memtable只是用来生成新的SSTable

再来看看如何compaction

SSTable,我们同样是通过 segment + compaction 来解决磁盘占用的问题。

segmentmemtableSSTable的时候就已经做了。

compaction则依赖后台线程定期执行了。但是对于有序的SSTable,我们可以使用归并排序的思路来合并和压缩文件:

故障恢复

如果在将memtable转存SSTable时,进程挂掉了,怎么保证未写入SSTable的数据不丢失呢?

参考数据库的redo log,我们也可以搞一个log记录当前memtable的写操作。在有Put请求过来时,除了写入memtable,还将操作追加到log。当memtable成功转成SSTable之后,它对应的log文件就可以删除了。在下次启动时,如果发现有残留的log文件,先通过它恢复上次的memtable

Bloom Filter

对于查询那些不存在的key,我们需要搜索完memtable和所有的SSTable,才能确定地说它不存在。

在数据量不大的情况下,这不是个问题。但是当数据量达到一定的量级后,这会对系统性能造成非常严重的问题。

我们可以借助Bloom Filter(布隆过滤器)来快速判断一个key是否存在。

布隆过滤器的特点是,它可能会把一个不存在的key判定为存在;但是它绝不会把一个存在的key判定为不存在。这是可以接受的,因为对于极少数误判为存在的key,只是多几次搜索而已,只要不会将存在的key误判为不存在就行。而且它带来的好处是显而易见的:可以节省大量的对不存在的key的搜索时间。

合并策略

上文已经提到,我们需要对SSTables做合并:将多个SSTable文件合并成一个SSTable文件,并对同一个key,只保留最新的值。

那这里讨论的合并策略(Compaction Strategy)又是什么呢?

A compaction strategy is what determines which of the sstables will be compacted, and when.

也就是说,合并策略是指:1)选择什么时候做合并;2)哪些SSTable会合并成一个SSTable

目前广泛应用的策略有两种:size-tiered策略和leveled策略。

  • HBase采用的是size-tiered策略。
  • LevelDB和RocksDB采用的是leveled策略。
  • Cassandra两种策略都支持。

这里简要介绍下两种策略的基本原理。后面研究LevelDB源码时再详细描述leveled策略。

size-tiered策略

简称STCS(Size-Tiered Compaction Strategy)。其基本原理是,每当某个尺寸的SSTable数量达到既定个数时,合并成一个大的SSTable,如下图所示:

它的优点是比较直观,实现简单,但是缺点是合并时的空间放大效应(Space Amplification)比较严重,具体请参考Scylla’s Compaction Strategies Series: Space Amplification in Size-Tiered Compaction。

空间放大效应,比如说数据本身只占用2GB,但是在合并时需要有额外的8G空间才能完成合并,那空间放大就是4倍。

leveled策略

STCS策略之所以有严重的空间放大问题,主要是因为它需要将所有SSTable文件合并成一个文件,只有在合并完成后才能删除小的SSTable文件。那如果我们可以每次只处理小部分SSTable文件,就可以大大改善空间放大问题了。

leveled策略,简称LCS(Leveled Compaction Strategy),核心思想就是将数据分成互不重叠的一系列固定大小(例如 2 MB)的SSTable文件,再将其分层(level)管理。对每个Level,我们都有一份清单文件记录着当前Level内每个SSTable文件存储的key的范围。

Level和Level的区别在于它所保存的SSTable文件的最大数量:Level-L最多只能保存 10 LSSTable文件(但是Level 0是个例外,后面再说)。

注:上图中,"run of"就表示一个系列,这些文件互不重叠,共同组成该level的所有数据。Level 1有10个文件;Level 2有100个文件;依此类推。

下面对照着上图再详细描述下LCS压缩策略:

先来看一下当Level >= 1时的合并策略。以Level 1为例,当Level 1SSTable数量超过10个时,我们将多余的SSTable转存到Level-2。为了不破坏Level-2本身的互不重叠性,我们需要将Level-2内与这些待转存的SSTable有重叠的SSTable挑出来,然后将这些SSTable文件重新合并去重,形成新的一组SSTable文件。如果这组新的SSTable文件导致Level-2的总文件数量超过100个,再将多余的文件按照同样的规则转存到Level-3

再来看看Level 0Level 0SSTable文件是直接从memtable转化来的:你没法保证这些SSTable互不重叠。所以,我们规定Level 0数量不能超过4个:当达到4个时,我们将这4个文件一起处理:合并去重,形成一组互不重叠的SSTable文件,再将其按照上一段描述的策略转存到Level 1

4 引用

[1] Patrick O’Neil, Edward Cheng, Dieter Gawlick, and Elizabeth O’Neil: “The Log- Structured Merge-Tree (LSM-Tree),” Acta Informatica, volume 33, number 4, pages 351–385, June 1996. doi:10.1007/s002360050048

[2] Fay Chang, Jeffrey Dean, Sanjay Ghemawat, et al.: “Bigtable: A Distributed Storage System for Structured Data,” at 7th USENIX Symposium on Operating System Design and Implementation (OSDI), November 2006.

转载于:https://juejin.im/post/5c99f0556fb9a070e82c1fcf

LSM存储引擎基本原理相关推荐

  1. plsql存储过程修改后怎么保存_分布式基础-存储引擎

    题目和文章内容有点不太符合,这里存储引擎是指单机存储引擎.对于分布式存储系统来说,存储引擎是必须的.存储引擎决定了数据在内存和磁盘中具体如何存储的,如何方便地拿出来的问题.可以说直接决定了存储系统的性 ...

  2. 哈希表企业应用-淘宝分布式文件系统核心存储引擎

    哈希表企业应用-淘宝分布式文件系统核心存储引擎-基本概述 淘宝网 谁都知道 是一个电子商务网站,可能是第一批电商模式 b2c 当然从一个技术人员讲只有一点 淘宝店铺商品存储到哪里 感觉有点像废话 :肯 ...

  3. LSM树(Log-Structured Merge Tree)存储引擎

    LSM树(Log-Structured Merge Tree)存储引擎 代表数据库:nessDB.leveldb.hbase等 核心思想的核心就是放弃部分读能力,换取写入的最大化能力.LSM Tree ...

  4. hbase 中的LSM树存储引擎

    LSM的原理:将对数据的修改增量保存在内存中,达到指定大小限制之后批量把数据flush到磁盘中,磁盘中树定期可以做merge操作,合并成一棵大树,以优化读性能.不过读取的时候稍微麻烦一些,读取时看这些 ...

  5. lsm mysql_LSM树(Log-StructuredMergeTree)存储引擎

    LSM树(Log-Structured Merge Tree)存储引擎 代表数据库:nessDB.leveldb.hbase等 核心思想的核心就是放弃部分读能力,换取写入的最大化能力.LSM Tree ...

  6. 字节跳动在 RocksDB 存储引擎上的改进实践

    本文选自"字节跳动基础架构实践"系列文章. "字节跳动基础架构实践"系列文章是由字节跳动基础架构部门各技术团队及专家倾力打造的技术干货内容,和大家分享团队在基础 ...

  7. 存储引擎 K/V 分离下的index回写问题

    前言 近期在做on nvme hash引擎相关的事情,对于非全序的数据集的存储需求,相比于我们传统的LSM或者B-tree的数据结构来说 能够减少很多维护全序上的计算/存储资源.当然我们要保证hash ...

  8. MongoDB Wiredtiger存储引擎实现原理——Copy on write的方式管理修改操作,Btree cache...

    转自:http://www.mongoing.com/archives/2540 传统数据库引擎的数据组织方式,一般存储引擎都是采用 btree 或者 lsm tree 来实现索引,但是索引的最小单位 ...

  9. 数据库存储引擎学习总结

    什么是存储引擎以及不同存储引擎特点 http://www.cnblogs.com/wildfox/p/5815414.html 以前一直玩Oracle数据库,整天围着业务需求和执行计划转,刚刚接触My ...

最新文章

  1. 机器学习对价格预测做模型与应用
  2. AUTOSAR从入门到精通100讲(四十八)-Lin通信协议栈分析两步走-LinTrcvLIN Driver
  3. Linux 系统应用编程——网络编程(常用命令解析)
  4. Colemak布局的实现 Window+Linux+Android
  5. ubuntu16.04安装virtualbox5.2
  6. 个人开源项目之快速检索算法
  7. 域用户登录方法在计算机上不被允许,如何解决不能交互式登录的问题
  8. python curl invalid syntax_将CURL Post转换为Python请求失败
  9. NVIDIA Jetson Xavier NX载板 RTSO-6002使用TF(MicroSD)卡重新刷机
  10. 谷歌企业邮箱:应用专用密码
  11. wifi中继的几种方法
  12. win7笔记本电脑做wifi热点
  13. 电脑BIOS为UEFI BIOS,出现蓝屏情况“你的设备遇到问题,需要重启。我们只收集某些错误信息,然后你可以重新启动。100%完成“,解决方法。
  14. 树莓派ubuntu mate 修改屏幕解析度为800x480
  15. 寻找不能拼读的汉语拼音
  16. 机器人方队解说词_运动会入场式方队解说词
  17. Java实现常见排序
  18. ssssssssssss
  19. 智能门锁:电源管理概述2
  20. 【ZYNQ Ultrascale+ MPSOC FPGA教程】第二十章 PS端RTC中断实验

热门文章

  1. 考研专业课,到底要不要报辅导班?
  2. 文本的检测、识别实战:使用 Tesseract 进行 OpenCV OCR 和文本识别
  3. 《JavaWeb视频教程》(p34)
  4. 大数据的应用场景都有哪些(医疗篇)
  5. 在 iphone 手机浏览器无法下载(主要指 safari 和 chrome ) excel ,但是可以直接预览 excel
  6. Ant Design Vue 如何获form表单里数据 并给 v-decorator绑定的数据重新赋值
  7. 永强教你加解密:对称篇(一)
  8. onkeypress、onkeydown、onkeyup
  9. Linux中断——request_irq
  10. 老友记全10集看完了,2个月的时光一晃而过!