当前位置: 首页 > news >正文

一文带你了解 LSM Compaction

作者:蚂蚁金服OceanBase数据库高级技术专家 华庭

当前,基于LSM架构的存储系统很常见,对该架构来说,compaction(合并)是一个重要操作,本文带你了解compaction相关知识。

10年前Google发表BigTable的论文,推动了基于LSM的系统架构的流行,用户数据写入先写WAL,再写Memtable,满足一定条件后冻结Memtable,执行转储操作形成一个数据文件SSTable。随着数据写入不断增多,转储次数也会不断增多,进而转储SSTable也会越来越多。然而,太多SSTable会导致数据查询IO次数增多,因此后台尝试着不断对这些SSTable进行合并,这个合并过程称为Compaction。Compaction分为两类:Minor Compaction和Major Compaction。

Minor Compaction是指选取一个或多个小的、相邻的转储SSTable与0个或多个Frozen Memtable,将它们合并成一个更大的SSTable。一次Minor Compaction的结果是更少并且更大的SSTable。

Major Compaction是指将所有的转储SSTable和一个或多个Frozen Memtable合并成一个SSTable,这个过程会清理被删除的数据。一般情况下,Major Compaction时间会持续比较长,整个过程会消耗大量系统资源,对上层业务有比较大的影响。                          

compaction放大因子

通常Compaction涉及到三个放大因子,Compaction需要在三者之间做取舍。

写放大

Write Amplification : 写放大,假设每秒写入10MB的数据,但观察到硬盘的写入是30MB/s,那么写放大就是3。写分为立即写和延迟写,比如redo log是立即写,传统基于B-Tree数据库刷脏页和LSM Compaction是延迟写。redo log使用direct IO写时至少以512字节对齐,假如log记录为100字节,磁盘需要写入512字节,写放大为5。

读放大

Read Amplification : 读放大,对应于一个简单query需要读取硬盘的次数。比如一个简单query读取了5个页面,发生了5次IO,那么读放大就是 5。假如B-Tree的非叶子节点都缓存在内存中,point read-amp 为1,一次磁盘读取就可以获取到Leaf Block;short range read-amp 为1~2,1~2次磁盘读取可以获取到所需的Leaf Block。

空间放大

Space Amplification : 空间放大,假设我需要存储10MB数据,但实际硬盘占用了30MB,那么空间放大就是3。有比较多的因素会影响空间放大,比如在Compaction过程中需要临时存储空间,空间碎片,Block中有效数据的比例小,旧版本数据未及时删除等等。

最近几年很多论文对LSM写放大有比较多的研究,而对于写放大本身而言,是一个很古老的问题,在计算机体系中,如果相邻两层的处理单元不一致或者应用对一致性等有特殊的需求,就很可能出现写放大问题。比如CPU Cache和内存cell,文件系统Block和磁盘扇区,数据库Block和文件系统Block,数据库redo/undo,文件系统journal等。下图是PebblesDB论文中测试了几款流行KV的放大系数对比:

RocksDB放大系数高达42倍,LevelDB也高达27倍。写放大意味着更多的读写,会造成系统性能波动,比如对SSD来说会加速寿命衰减,从成本角度说也更加耗电,采用更优的避免写放大的算法可以使用成本更低廉的SSD,写放大还影响数据库系统的持续写入的带宽,假如磁盘带宽为500M/s,写放大为10,那么数据库持续写入的的最大带宽为50M/s,所以解决写放大就成了一个很重要的问题。

Facebook在Fast 14提交的论文《Analysis of HDFS Under HBase: A Facebook Messages Case Study》所提供的一些测试结论:在Facebook Messages业务系统中,业务读写比为99:1,而最终反映到磁盘中,读写比却变为了36:64。

放大问题的本质是一个系统对“随时全局有序"的需求有多么的强烈。所谓随时,就是任何的写入都不能导致系统无序;所谓全局,即系统内任意元单位之间都要保持有序。B-Tree系列是随时全局有序的典型代表,而Fractal Tree打破了全局的约束,允许局部无序,提升了随机写能力;LSM系列进一步打破了随时的约束,允许通过后台的Compaction来整理排序。在LSM这种依靠后台整理来保序的系统里面,系统对序的要求越强烈,写放大越严重。

要同时将写放大,读放大,空间放大优化到最优是很难的,比如SSD内部的碎片整理,碎片越多,空间放大和读放大越严重,为了改善空间放大和读放大,将page中的有效数据拷贝到新的page里(copy-out),这样会带来写放大,如果page中有效数据率越低,那么空间放大越大,copy-out的写放大越低,反之,空间放大越小,copy-out写放大越大。

compaction的作用与副作用

随着数据写入不断增加,Minor Freeze不断触发,转储数据不断增多,一次查询可能需要越来越多的IO操作,读取延时也在不断变大。而执行Minor Compaction会使得转储文件数基本稳定,进而IO Seek次数会比较稳定,延迟就会稳定在一定范围。然而,Minor Compaction操作重写文件会带来很大的带宽压力以及短时间IO压力。因此可以认为,Minor Compaction就是使用短时间的IO消耗以及带宽消耗换取后续查询的低延迟。

为了换取后续查询的低延迟,除了短时间的读放大之外,Compaction对写入也会有很大的影响。当写请求非常多,导致不断触发Minor Compaction生成转储SSTable,多次Minor Freeze会触发Major Freeze,从而导致Major Compaction,但Compaction的速度远远跟不上数据写入速度,Memtable内存不足时,会限制用户数据写入。如果Compaction消耗大量IO和带宽,也会导致读性能急剧下降。为了避免这种情况,在Memtable内存紧张时候会限制写请求的速度。

Minor Compaction释放Memtable内存,清理不必要的多版本数据,同时保证转储SSTable数量稳定,降低读延迟;Major Compaction清除删除和过期的数据,有效降低存储空间,也可以有效降低读取时延。Compaction会使得数据读取延迟一直比较平稳,但付出的代价是大量的读延迟毛刺和一定的写阻塞。

compaction目标

Compaction的设计总是会追求一个平衡点,一方面需要保证Compaction的基本效果,另一方面又不会带来严重的IO压力。然而,并没有一种设计策略能够适用于所有应用场景或所有数据集。Compaction选择什么样的策略需要根据不同的业务场景、不同数据集特征进行确定。设计Compaction策略需要根据业务数据特点,目标就是降低各种放大,不断权衡如下几点:

合理控制读放大:避免因Minor Freeze增多导致读取时延出现明显增大,避免请求读取过多SSTable;

合理控制写放大:避免一次又一次地Compact相同的数据;

合理控制空间放大:避免让不需要的多版本数据,已经删除的数据和过期的数据长时间占据存储空间,避免在Compaction过程中占用过多临时存储空间,及时释放已经Compact完成的无用SSTable的存储空间;

控制Compaction使用的资源:在业务高峰期少使用资源,业务低峰期使用更多的资源;

控制Compaction平滑度:在一定的负载下,业务的响应时间和吞吐量稳定可预期;

compaction算法演进

Memtable(增量数据) + L1(基线数据)

这是一种比较理想的场景,机器的内存比较大,磁盘空间和业务数据比较小,业务每天的修改几乎可以全部存储在Memtable中,采用每日Major Compaction的方式将增量数据与基线数据进行合并,如下图所示:


每张表包含一个增量数据Memtable和一个基线数据SSTable,这种每日Major Compaction算法比较简单,point read-amp为1,short range read-amp为1,空间放大为1,写放大为sizeof(database) / sizeof(memtable) + 1。这种Compaction算法保证最优读放大和空间放大,在Memtable内存比较大,磁盘空间和业务数据不大的情况下,写放大也可以接受,但随着业务数据量的增长,采用压缩率更高的压缩算法,以及单机存储空间的快速增长,写放大变得非常巨大,每日Major Compaction的速度变得越来越慢。

Memtable(增量数据)+L0(增量转储数据)+L1(基线数据)

随着业务的发展,每日更新的数据越来越大,Memtable已经不能存储整天的修改数据,当系统内存紧张时,触发Minor Freeze,然后转储生成SSTable,如下图所示:



point read-amp为>=1次磁盘读取和多次bloomfilter过滤,short range read-amp为L0+L1 SSTable数量总和次磁盘读取,写放大为sizeof(database) / sizeof(每日增量数据量) + 2 (redo log,L0);最近几年单机的内存量几乎没有增加,但业务基线数据总量和每天的增量数据增长巨大,所以使用每日Major Compaction算法写放大非常大。随着L0层SSTable个数增加,读放大不断增加,为了限制L0层SSTable的个数,当转储达到一定次数后,发起一次Major Freeze,然后启动Major Compaction。这种策略会导致每天需要执行多次Major Compaction,随着基线数据量的增长,写放大越来越大,Major Compaction也越来越慢。

根据场景选择Compaction策略

在不同的场景如何折中选择读放大,写放大和空间放大呢?在系统中统一采用一种Compaction策略往往比较难兼顾各种细节需求,而针对不同表配置不同的Compaction策略能更好的适应表的读写负载。

递增rowkey的insert/update/delete,无论读写比例多少

最优写放大和空间放大,无读放大,采用Memtable + L1模式,如果delete range比较多,则在Memtable中标记delete range。

write only,大量随机rowkey insert/update/delete

优化写放大,采用Memtable + L0 + L1模式,每次转储生成一个SSTable,L0的总大小与L1相同时进行Major Compaction写放大最小。

write mostly,大量随机rowkey insert/update/delete

优化写放大,容忍稍大的读放大,采用Memtable + L0 + L1模式,L0层累积多次Major Freeze的增量SSTable数据,L0层包含多个SSTable,按照优化写放大策略控制L0层SSTable数量和大小,使用Stripe Compaction和渐进合并策略减少Major Compaction的写放大和临时空间放大。

read only,或write很少

最优写放大,读放大和空间放大,采用Memtable + L1模式,Memtable没有数据或只有少量数据。

read mostly,少量随机rowkey insert/update/delete

优化读放大,采用Memtable + L0 + L1模式,每次转储Frozen Memtable与前一个L0层SSTable合并成一个新的L0层SSTable,如果delete range比较多,则在Memtable和L0层SSTable中标记delete range,L0与L1进行Major Compaction的写放大比较小。

read mostly,大量随机rowkey insert/update/delete

优化写放大和读放大,采用Memtable + L0 + L1模式,L0层累积多次Major Freeze的增量SSTable数据,L0层包含多个SSTable,根据用户的访问热点决定哪些range内的L0层SSTable合并,如果delete range比较多,则在Memtable和L0层SSTable中标记delete range,将L0和L1分成多个sub-range条带,每个sub-range积累了足够增量数据后才进行Major Compaction,类似渐进合并的方式,每次Major Freeze都合并L0和L1的部分range数据。如果delete比较多,空间放大不优,不能即使释放已经删除数据的存储空间。


相关文章:

  • 里氏替换原则
  • UI设计不就是画线框,凭什么年薪30W?
  • 彻底解决乱码
  • 我的微软最有价值专家(Microsoft MVP)之路
  • 如何向Android模拟器打电话发短信
  • 【奥斯卡理财星体系 序篇】为什么你需要学习这个理财体系?
  • 基于阿里云快速搭建数字营销引擎【计算广告】
  • wcf深入学习笔记
  • Eclipse常用快捷键之代码编辑篇
  • QQ客服代码
  • 完美解决gradle下载慢的问题
  • 人物四(奥瑞夫特)
  • java.lang.OutOfMemoryError: PermGen space
  • 1.揭开消息中间件RabbitMQ的神秘面纱
  • 文本框不够长,显示“XXX...”
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  •  D - 粉碎叛乱F - 其他起义
  • Java|序列化异常StreamCorruptedException的解决方法
  • Java多线程(4):使用线程池执行定时任务
  • Joomla 2.x, 3.x useful code cheatsheet
  • mac修复ab及siege安装
  • Python3爬取英雄联盟英雄皮肤大图
  • Python十分钟制作属于你自己的个性logo
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • TypeScript迭代器
  • Vue ES6 Jade Scss Webpack Gulp
  • Vue实战(四)登录/注册页的实现
  • 产品三维模型在线预览
  • 服务器从安装到部署全过程(二)
  • 给Prometheus造假数据的方法
  • 回流、重绘及其优化
  • 解决iview多表头动态更改列元素发生的错误
  • 驱动程序原理
  • 数据结构java版之冒泡排序及优化
  • 白色的风信子
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​LeetCode解法汇总518. 零钱兑换 II
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (09)Hive——CTE 公共表达式
  • (13):Silverlight 2 数据与通信之WebRequest
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (九)c52学习之旅-定时器
  • (六)Hibernate的二级缓存
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (图)IntelliTrace Tools 跟踪云端程序
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转)Linux NTP配置详解 (Network Time Protocol)