热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

KVSSD:结合LSM与FTL以实现写入优化的KV存储

本次分享的Paper[1]:《KVSSD:CloseintegrationofLSMtreesandflashtranslationlayerforwrite-efficientK

本次分享的 Paper[1]:《 KVSSD:Close integration of LSM trees and flash translation layer for write-efficient KV store 》是在 18 年的 Design, Automation & Test in Europe Conference & Exhibition (DATE) 会议上出现的 KVSSD,作者为:Sung-Ming Wu[2]、Kai-Hsiang Lin[3]、 Li-Pin Chang[4]。

这篇 Paper 主要思路是在 SSD 上直接提供 KV 接口,将 LSM Tree 与 FTL 深度结合,从而避免从 LSM Tree,主机文件系统到 FTL 多个软件层的写入放大。跟大家分享这篇 Paper ,一方面是蹭一蹭 KV 接口已经成功进入 NVMe 2.0 规范被标准化的热点,另一方面是为了和 TiKV / TiDB 的同学探讨未来存储硬件的更多可能性,希望能带来一些启发。

本文将首先介绍问题的背景,什么是写放大,哪里产生了写放大,然后引出解决方案,介绍 KVSSD 做了哪些优化,之后再介绍 KVSSD 的性能评估数据,以及工业上的进展。

file


背景

首先我们来聊聊背景。这是一个 TiKV 的架构图:

file

我们都知道 TiKV 是一个分布式的 Key-Value 数据库,TiKV 的每一个节点都运行着一个 RocksDB 的实例,就像是这样:

file

RocksDB 基于 Log-structed merge-tree (LSM) 开发的,LSM tree 是目前业界运用最为广泛的持久化数据结构之一。我们要讨论的问题就是 LSM tree 在 SSD 上遇到的写入放大问题及其解决方案。


KV 存储系统中的写放大

从存储的视角来看,一个 KV 存储的软件堆栈大概是这样的:

file

最顶层是一颗 LSM tree,具体的实现就不展开了。大体的思路是在内存中维护可变的 Memtable,在 SSD 上维护不可变的 SSTable。Memtable 写满后会作为 SSTable 输入存储,而所有的 SSTable 会组合成一颗分层的树,每一层写满后就会向下一层做 compaction。LSM tree 维护过程中产生的 IO 会通过文件系统与 BIO 层的转换落到 SSD 上。

file

然后看一下文件系统。显然的,文件落到文件系统上必然会有一些额外的开销,比如说我们需要维护文件的 Metadata。inode、大小、修改时间、访问时间等都需要持久化。此外,文件系统需要能够保证在 crash 的时候不丢失已经写入的数据,所以还需要引入日志,写时复制这样的技术。

file

然后再来看看 SSD 这一层。一个典型的 NAND Flash 芯片通常由 package、die、plane、block 和 page 组成。package,又叫做 chips,就是我们能在 SSD 上看到的颗粒。一个 pakage 通常由多层堆叠而成,每一层就叫做一颗 die。一颗 die 里面会被划分为多个 plane,而每个 plane 里面会包括一组 Block,每个 Block 又可以进一步细化为 Page。其中 Block 是擦除动作的最小单位,通常是 128KB 到 256KB 不等,而 Page 是读取和写入的最小单位,通常为 2KB 到 32KB 不等。为了维护逻辑地址到物理地址的转换,SSD 中引入了 FTL。此外,FTL 还需要承担垃圾回收,坏块回收,磨损均衡等职责。

我们知道 SSD 设备的特性决定了它在写之前必须要进行擦除操作,设备需要将数据全部读到内存中修改并写回。为了均衡芯片损耗与性能,SSD 通常会选择标记当前 block ,寻找新的可用 block 来写入,由 FTL 来执行垃圾回收,这也就是我们常说的 SSD Trim 过程。

file

从上面的分析我们不难观察到严重的写放大问题:



  • LSM tree 的 compaction 过程



  • 文件系统自身



  • 块请求落到 FTL 上会出现的 read-modify-write 过程



  • FTL 的垃圾回收



  • Paper 中没有考虑文件系统本身的写放大,只取了剩下三点的乘积作为总体的写入放大率。写入放大显然是个坏东西,既加大了存储设备的磨损,又降低了写入的吞吐,这些因素最终都会反映到用户的总体持有成本上。




如何缓解写放大?

为了解决或者说缓解这个问题,大家提出过很多不同方向的方案。

file

比如说我们可以对算法做一些改造,比如 LSM-trie[5]、PebblesDB[6] 或者 WiscKey[7]。WiscKey 大家可能比较熟悉一点,将 LSM tree 中的 Key 和 Value 分开存储,牺牲范围查询的性能,来降低写放大。TiKV 的 titan 存储引擎、dgraph 开源的 badger[8]还有 TiKV 社区孵化中的 Agatedb[9] 都是基于这个思路设计的。

或者我们也能在文件系统这一层做些事情,比如说专门开发一个面向写方法优化的文件系统,减少在日志等环节的写入 IO,比如说开启压缩比更高的透明压缩算法,或者面向 KV 的典型负载做一些优化和调参。

但是算法上和软件上的优化终究还是有极限的,想要突破就只能不做人啦,直接对硬件下手,从固件的层面进行优化。一般的来说,系统优化都有拆抽象和加抽象两个方向的优化。拆抽象是指我们去掉现有的抽象,将更多的底层细节暴露出来,这样用户可以更自由的根据自己的负载进行针对性优化。加抽象是指我们增加新的抽象,屏蔽更多的底层细节,这样可以针对硬件特点做优化。

存储系统的优化也不例外。

file

拆抽象思路的先驱者是 Open-Channel SSD,它的思路是把固件里的 FTL 扬了,让用户自己来实现。这个思路是好的,但是 Open-Channel Spec 只定义了最通用的一部分,具体到厂商而言,他们考虑到自己 SSD 的产品特性和商业机密,往往选择在兼容 Open-Channel Spec 的同时,再加入一些自己的定义。这就导致至今都没有出现通用的 Open-Channel SSD 和针对业务的通用 FTL。对用户来说,并不通用的 Open-Channel SSD 带来了更严重的 vendor-lock 问题。所以 Open-Channel SSD 迟迟无法得到大规模应用。

NVMe 工作组也看到了这样的问题,所以他们消化吸收了 Open-Channel 的精髓,提出了 Zoned Namespace (ZNS) 的新特性。ZNS 将一个 namespace 下的逻辑空间地址划分为一个个的 zone,zone 当中只能进行顺序写,需要显式的擦除操作才能再次进行覆盖写。通过这种方式 SSD 将内部结构的边界透露给外界,让用户来实现具体的地址映射,垃圾回收等逻辑。

另一个思路是加抽象,既然上层业务做不好这个事情,那就把它加到固件里面,硬件自己做。比如说在固件中直接实现 Key-Value 接口,或者使用计算型 SSD 将更多的计算任务下推到 SSD 上来做。

这里额外插一句感慨,三国演义开篇的“天下大势,分久必合,合久必分”真是太对了。软件定义存储发展到现在,硬件厂商也不甘于成为纯粹的供货商,他们也想加入到产业链上游,获取更多的利润。所以在存算分离已经成为大势所趋的时候,业界还孕育着一股存算融合的潮流:通过更合理更规范的抽象,充分利用自己软硬一体的优势,提供在延迟和吞吐上更具优势的产品。比如有消息称三星开发中的 KVSSD 上搭载的芯片计算能力相当于两三年前的手机芯片。考虑到 FPGA 和 ARM 这样精简指令集的芯片的持续发展,相信这股潮流会带来更多系统架构上的可能性。


KVSSD 设计

好的,回归正题。这篇 Paper 就是采用了加抽象的路线,在固件中直接实现 Key-Value 接口。

file

KVSSD 采用了闪存原生的 LSM tree 实现,叫做 nLSM (NAND-flash-LSM)。nLSM tree 把 FTL 的 L2P(Logical To Physic) 层转换为了 K2P ( Key To Physic) 映射,每个树节点都代表一个 SSTable 的 Key 范围。nLSM tree 将整个闪存花费为元数据区和 KV 区,KV 区中存储排序后的 KV 对,元数据区中的每一页叫做元数据页,只包含指向 KV 页和键范围的指针。

nLSM 运用了如下设计来优化写放大:


K2P Mapping

file

首先我们来看一下 K2P 映射的设计。显然的,K2P 的抽象层次比 L2P 高很多,不可能在 SSD 的内存中直接存储所有 Key 对应的物理 Page。所以作者选择了在 K2P 中使用 Key Range Tree 来存储 彼此不相交的 key-range 到 SSTable 的映射。nLSM tree 使用 Key Range Tree 来找到一个元数据页,然后使用元数据页中的 key range 信息来找到一个 KV 页,然后再从 KV 页中检索目标 KV 对。nLSM tree 给每个 SSTable 分配了一个闪存块,闪存块的大小是 4MB 跟 SSTable 的大小一样大,保证 SSTable 物理上连续且跟页面边界对齐。在 compaction 的时候,nLSM tree 会对旧的 SSTable 进行多向合并排序,并写入新的 SSTable,并丢弃旧的 SSTbale。之后的垃圾回收过程可以直接擦除这些块,不需要进行任何的数据复制。


Remapping Compaction

file

其次是 Remapping Compaction。

假设我们现在 Key Range 被划分为 A 到 I 这几个区间。Ta 表示的是 Level i 层的数据,Tb 和 Tc 表示 Level i+1 层的数据,现在我们要进行 Compaction 的话,就需要以某种形式将 Ta 中的数据塞进 Tb 和 Tc。Tb 和 Tc 组成一一组区间连续的 Key,而 Ta 跟他们都有一些重叠的地方。如果按照传统的方式重写这些 Page 的话,我们需要写 12 个 KV page,再加上 3 个 metadata page。但是在 Remapping Compaction 中,会选择重新写 Tx、Ty、Tz 三个 metadata page 分别指向已经存在的 KV Page。这样就把重写 page 的代价从 15 降低到了 3。


Hot-cold Separation

最后是冷热分离。显然的,高效的垃圾回收依赖数据分布的特征。假如相对较冷的数据能分布在一起,避免重复的 gc 热数据,可以极大的降低 gc 的写入放大。在 LSM tree 的写入模型当中,上层总是比下层的数据要小,换句话来说,上层的数据参与 gc 更多、更频繁。不同层次的数据有着不同的生命周期。基于这样的特性,有一个可能优化是在垃圾回收迁移数据的时候,尽可能的在同一级别的 KV 页中写入数据,这样能保证相似寿命的页面能被分组到相似的区块中。


KVSSD 性能分析

接下来我们看看 Paper 的性能分析环节。这篇 Paper 主要涉及三个性能方面的因素:写放大、吞吐和读放大。实验中使用的 SSD 设备是 15GB,5% 是保留空间,page size 是 32KB,block size 是 4MB。实验的方法是使用 blktrace 记录 leveldb 的 block I/O trace 然后在 SSD 模拟器上重放来收集闪存的操作数据。分别对比了



  • LSM: 基于 leveldb



  • dLSM: 一种 delay compaction 的优化



  • lLSM: 一种轻量级 compaction 的优化



  • nLSM



  • rLSM(-): nLSM with remapping compaction



  • rLSM: nLSM with remapping compaction and hot-cold sparation



file

file

根据这一组图可以看到,在给定的测试条件下 rLSM 能将写放大降低至原来的 12%,同时将吞吐提升了 4.47 倍,但是带来了 11% 的读放大。

好,到这里我们这篇 paper 的整体思路就已经介绍完了。我们来回顾一下 KVSSD 的价值,首先最明显的是 KVSSD 能带来更小写入放大,可以提高吞吐,进而降低 TCO,符合现在业界降本增效的潮流。其次从系统架构设计的角度上来看,KVSSD 能够进一步的将 I/O Offload 到 SSD 设备上。以 rocksdb 为例,使用 KVSSD 能降低 rocksdb 的 compaction 和 log 开销。此外,SSD 的计算能力实际上是在逐步提升的,未来可以在 SSD 中进行压缩,加密,校验等一系列重计算的任务。这些都为架构提供了新的可能性。


KVSSD 在工业上的进展

最后我们来看看业界的跟进情况。

在 2019 的 SYSTOR 会议上,三星沿用这一思路,发表了论文 Towards building a high-performance, scale-in key-value storage system[10],在论文中提到三星与 SNIA 联手制定了 Key Value Storage API 规范,基于现有的 Block SSD 实现了 KV-SSD 的原型:

file

还发表了公开的 KVSSD 相关的 API 与驱动:

https://github.com/OpenMPDK/KVSSD

根据三星论文中的分析来看,KV-SSD 展现了非常强的线性扩展能力,随着设备数量的增加,系统整体的 TPS 成线性增长,基本不受 CPU 的限制,感兴趣的同学可以找来看看。

这里我补充一下,三星的 KV-SSD 跟本次分享的论文只是大体思路相同,具体的设计和实现上还是有很大差异。

在 2021 年 6 月发布的 NVMe 2.0 规范中,KVSSD 相关的指令集已经被规范化为 NVMe-KV 指令集,成为新的 I/O 命令集之一,允许使用 Key 而不是 Block 地址来访问数据。考虑到业界对 NVMe 规范的广泛支持,预计完全支持 NVMe 2.0 的 SSD 很快就有商用的产品上市,希望大家保持关注。


Q&A

业界更关心 ZNS 还是 KVSSD?

ZNS 实现上要比 KVSSD 容易的多,成本也比 KVSSD 更好控制,所以目前业界对 ZNS 还是更热心一点。


KVSSD 冷热分离的设计是不是会导致 Block 擦写不均衡?

是的,论文里面没有展开论述相关的细节。按照目前这样的设计确实会导致这个问题,需要在实现的时候讲磨损均衡的问题也考虑进来。


KVSSD 需要占用宿主机的内存和 CPU 吗?

不需要,KVSSD 自带独立的内存和处理芯片,不依赖宿主机的资源。这也是使用 KVSSD 的意义之一:我们可以将这部分的负载 Offload 到 SSD 上,使得单一宿主机上可以接入更多的设备。


引用链接

[1] IEEE: https://ieeexplore.ieee.org/document/8342070

[2] Sung-Ming Wu: https://ieeexplore.ieee.org/author/37086370119

[3] Kai-Hsiang Lin: https://ieeexplore.ieee.org/author/37086098744

[4] Li-Pin Chang: https://ieeexplore.ieee.org/author/37733936200

[5] LSM-trie: https://www.usenix.org/conference/atc15/technical-session/presentation/wu

[6] PebblesDB: https://www.cs.utexas.edu/~vijay/papers/sosp17-pebblesdb.pdf

[7] WiscKey: https://www.usenix.org/conference/fast16/technical-sessions/presentation/lu

[8] badger: https://github.com/dgraph-io/badger

[9] Agatedb: https://github.com/tikv/agatedb

[10] Towards building a high-performance, scale-in key-value storage system: https://dl.acm.org/doi/10.1145/3319647.3325831


作者

丁皓 青云科技存储工程师


本文由博客一文多发平台 OpenWrite 发布!




推荐阅读
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 手指触控|Android电容屏幕驱动调试指南
    手指触控|Android电容屏幕驱动调试指南 ... [详细]
  • 基于iSCSI的SQL Server 2012群集测试(一)SQL群集安装
    一、测试需求介绍与准备公司计划服务器迁移过程计划同时上线SQLServer2012,引入SQLServer2012群集提高高可用性,需要对SQLServ ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 在使用 Cacti 进行监控时,发现已运行的转码机未产生流量,导致 Cacti 监控界面显示该转码机处于宕机状态。进一步检查 Cacti 日志,发现数据库中存在 SQL 查询失败的问题,错误代码为 145。此问题可能是由于数据库表损坏或索引失效所致,建议对相关表进行修复操作以恢复监控功能。 ... [详细]
  • 本文详细解析了 Android 系统启动过程中的核心文件 `init.c`,探讨了其在系统初始化阶段的关键作用。通过对 `init.c` 的源代码进行深入分析,揭示了其如何管理进程、解析配置文件以及执行系统启动脚本。此外,文章还介绍了 `init` 进程的生命周期及其与内核的交互方式,为开发者提供了深入了解 Android 启动机制的宝贵资料。 ... [详细]
  • ### 优化后的摘要本学习指南旨在帮助读者全面掌握 Bootstrap 前端框架的核心知识点与实战技巧。内容涵盖基础入门、核心功能和高级应用。第一章通过一个简单的“Hello World”示例,介绍 Bootstrap 的基本用法和快速上手方法。第二章深入探讨 Bootstrap 与 JSP 集成的细节,揭示两者结合的优势和应用场景。第三章则进一步讲解 Bootstrap 的高级特性,如响应式设计和组件定制,为开发者提供全方位的技术支持。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 在Android平台中,播放音频的采样率通常固定为44.1kHz,而录音的采样率则固定为8kHz。为了确保音频设备的正常工作,底层驱动必须预先设定这些固定的采样率。当上层应用提供的采样率与这些预设值不匹配时,需要通过重采样(resample)技术来调整采样率,以保证音频数据的正确处理和传输。本文将详细探讨FFMpeg在音频处理中的基础理论及重采样技术的应用。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 利用ZFS和Gluster实现分布式存储系统的高效迁移与应用
    本文探讨了在Ubuntu 18.04系统中利用ZFS和Gluster文件系统实现分布式存储系统的高效迁移与应用。通过详细的技术分析和实践案例,展示了这两种文件系统在数据迁移、高可用性和性能优化方面的优势,为分布式存储系统的部署和管理提供了宝贵的参考。 ... [详细]
  • 第二章:Kafka基础入门与核心概念解析
    本章节主要介绍了Kafka的基本概念及其核心特性。Kafka是一种分布式消息发布和订阅系统,以其卓越的性能和高吞吐量而著称。最初,Kafka被设计用于LinkedIn的活动流和运营数据处理,旨在高效地管理和传输大规模的数据流。这些数据主要包括用户活动记录、系统日志和其他实时信息。通过深入解析Kafka的设计原理和应用场景,读者将能够更好地理解其在现代大数据架构中的重要地位。 ... [详细]
author-avatar
mobiledu2502916503
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有