3年前 (2021-05-21)  相关技术 |   抢沙发  1421 
文章评分 1 次,平均分 5.0

RocksDB是一种流行的可嵌入持久键值存储。作为Google LevelDB项目的一个分支,Facebook于2012年首次开源,多年来它已经适应了各种工作负载,包括数据库存储引擎和应用程序数据缓存。

在本文中,我们将解释选择RocksDB作为YugabyteDB的基础构建块的基本原理。我们还将重点介绍如何将YugabyteDB中的行建模为文档,然后将其存储为RocksDB中的多个键值对。

为什么一个数据库需要另一个数据库?

您可能想知道为什么像YugabyteDB这样的数据库依赖于另一个像RocksDB这样的数据库。虽然两者的名称中都有字母“DB”,但它们满足的需求却大不相同。

RocksDB是一个整体式的键值存储引擎,具有较低级别的api。例如,键/值只是字节数组而不是类型化的。它设计用于在单个节点的上下文中快速访问、持久化和嵌入。考虑到单节点的关注点,它没有将高可用性和地理数据分布作为设计目标。

另一方面,YugabyteDB是一个分布式SQL数据库,用于构建internet规模的、全局分布的应用程序,具有访问不同节点上的密钥的多分片(又称分布式)事务等特性。这些节点可以在单个数据中心/区域中,也可以在不同区域中相距很远(区域之间的广域网延迟不可预测)。YugabyteDB支持YSQL,一个用于扩展RDBMS工作负载的完全关系型sqlapi,以及YCQL,一个用于internet工作负载的基于SQL的灵活模式API。

分布式SQL数据库是由多个组件组成的软件的复杂集成:

  • API层负责特定语言的查询编译、执行和优化
  • 复制和事务协调
  • 数据分片和负载平衡
  • 故障检测和恢复组件
  • 最后但并非最不重要的是,每节点(或节点本地)存储引擎

具体来说,YugabyteDB由两个高层组成–Yugabyte查询层(YQL)是API层,DocDB是分布式文档存储。

基于RocksDB实现的高性能文档存储框架:YugabyteDB

YQL实现了YugabyteDB支持的api,并在DocDB上运行,DocDB是所有yqlapi中通用的数据存储。

无论数据库是分布式的还是单片的,设计良好的单节点存储引擎对数据库的可靠性、效率和性能都至关重要。这就是DocDB使用高度定制版本RocksDB作为其每节点存储引擎的确切原因。这个引擎针对高性能和大数据集进行了充分优化。对RocksDB所做的所有更改都作为开放源码apache2.0项目YugabyteDB的一部分分发。

为什么是RocksDB?

在RocksDB开发初期,我们是Facebook的数据基础设施工程师。至少可以说,看着RocksDB在我们面前发展成为低延迟和高吞吐量存储(如flashssd)的理想数据存储是令人兴奋的。

当我们在2016年初启动YugabyteDB项目时,RocksDB已经是一个成熟的项目,这要归功于它在Facebook和包括雅虎和LinkedIn在内的其他网络规模公司的广泛采用。这让我们有信心把它作为YugabyteDB的起点。

我们决定使用ROCKSDB的另外两个关键因素是日志结构合并树LSM)设计和我们在C++中实现YugabyteDB的愿望。

LSM vs.B-Tree:有明确的赢家吗?

如今,像RocksDB这样的LSM存储引擎已经成为处理具有快速增长数据的工作负载的事实上的标准,尤其是在SSD越来越便宜的情况下。

Google的OLTP数据库,即panner及其前身BigTable,使用LSM存储。Facebook的用户数据库从使用带有InnoDB(B-Tree)引擎的MySQL迁移到使用基于RocksDB的MyRocks(LSM)引擎的MySQL。MongoDB的WiredTiger存储引擎也提供了LSM选项,尽管MongoDB本身不支持该选项。较新的时间序列数据存储(如XDB)也遵循LSM设计。

正如前面在“繁忙的开发人员数据库存储引擎指南”中所描述的,LSM在B树上取得巨大成功的背后有两个主要原因。

第一个原因是LSM引擎中的写入是顺序的,ssd处理顺序写入的效果比随机写入要好得多。顺序写入极大地简化了SSD层的垃圾收集,而随机写入(如在B树引擎中)会导致SSD碎片。碎片整理过程会显著影响SSD性能。

第二,尽管传统的观点认为B-tree更适合于阅读密集型工作负载,但现实情况是,这种观点对于许多现代工作负载来说是不准确的。

  • LSM通常需要更多的驱动器读取IOPS。然而,与hdd不同,ssd在微秒延迟下提供非常高的读取吞吐量,这是现代硬件的一个主要问题。
  • 对于随机读取工作负载,bloom过滤器通过牺牲一些CPU来帮助减少读取IOPS的数量,使其接近于B-Tree样式的工作负载。
  • 对于读取最近的工作负载(例如,“获取收件箱前50条消息或设备最近6小时的活动”),lsm的性能优于B-Trees。这是因为在LSM中,数据自然是时间分区的,而仍然是通过诸如“用户id”(在类似收件箱的应用程序中)或“设备id”(如在物联网或时间序列应用程序中)来聚集的。

语言问题:C++、java还是GO?

ROCKSDB是用C++编写的,目的是利用快速SSDS和非易失性存储器(NVM)的完整吞吐量和低延迟。出于类似的原因,在C++中实现了YuGabyTeDB。

我们不想在数据库引擎IO的关键路径中设置跨语言切换。例如,CoRoChanDB,另一个使用ROCKSDB作为其存储引擎的数据库,在关键IO路径中引起GO <= > C++切换,因为查询执行层是在GO语言和存储引擎(ROCKSDB)中的C++。处理相当简单的查询所需的这些语言边界跳转的数量仍然很大,这是对性能产生不利影响的因素之一。

我们抵制了用垃圾收集语言(如Java(如apachecassandra)或Go(如CockroachDB))开发高性能数据库的诱惑。这样的实现无法充分发挥快速存储的潜力,也无法利用大量RAM。

由于YuGabyTeDB和ROCKSDB都是C++,我们能够实现ROCKSDB与系统的其余部分的深入和低开销集成。这种集成允许我们在系统的各个组件之间尽可能高效地传递数据(通常是通过引用)。

DocDB简介

如前所述,DocDB是YugabyteDB的分布式文档库。不管使用哪种API,YugabyteDB管理的每一行都作为单独的文档存储在DocDB中。这使得YugabyteDB拥有一个可插入的API架构,在这个架构中,可以在公共文档数据模型之上添加新的API。每个DocDB文档都映射到RocksDB中的多个键值对,RocksDB是底层的每节点存储引擎。

数据类型

DocDB扩展了RocksDB,可以有效地处理包含基本类型和非基本类型(如map、collections和JSON)的文档。DocDB中的值可以是:

  • 基本类型:如int32、int64、double、text、timestamp等。
  • 非基本类型(存储为排序映射):将标量子键映射到值的对象,这些值本身可以是标量或嵌套对象类型。

将文档映射到RocksDB键值

DocDB数据模型允许多级嵌套,并对应于类似JSON的格式。其他数据结构,如列表、映射和排序集,都是使用DocDB的对象类型和特殊的键编码实现的。

例如,考虑存储在DocDB中的以下文档:

user-123 = {
	addr = {
		city = San Francisco
		zip = 94103
	},
	age = 36
}

上述文档作为一组密钥存储在RocksDB中。RocksDB中存储的每个密钥都由许多组件组成,其中第一个组件是“文档密钥”,后面是一些标量组件,最后是MVCC时间戳(按相反顺序排序)。当我们对键中的原语值进行编码时,我们使用二进制可比较编码,这样RocksDB中的排序顺序最终就是所需的顺序。

假设上面的示例文档完全是在时间T10编写的。在内部,它使用五个RocksDB键值对进行存储:

user-123, T10 -> {} // This is an init marker
user-123, addr, T10 -> {} // This is a sub-document init marker
user-123, addr, city, T10 -> San Francisco
user-123, addr, zip, T10 -> 94103
user-123, age, T10 -> 36

DocDB使用RocksDB作为只附加的存储,这样就可以通过在相应级别上编写一个tombstone标记来删除文档或嵌套文档。定制的读挂钩自动识别这些特殊标记并抑制过期数据。子文档中的过期值由我们定制的压缩挂钩清除/垃圾收集。

以低延迟和高吞吐量服务工作负载

DocDB通过使用多种技术确保以尽可能低的成本执行数据库读/写操作。以下是几个例子:

只对行(如ysqlapi中的列)或集合(如ycqlapi中的元素)的一小部分进行细粒度更新,而不会对整个行或集合产生读-修改-写惩罚。

在任意嵌套级别删除/覆盖行或集合/对象,而不产生读取惩罚,以确定需要删除的特定KVs集

高效的行/对象级TTL处理,通过紧密连接到底层RocksDB KV存储的“读取/压缩”层。

总结

RocksDB可以说是过去十年中最成功的开源数据基础设施项目之一。在快速单片键值存储方面,它是首屈一指的。YugabyteDB利用RocksDB的优势,将其嵌入到分布式文档库DocDB的每节点存储引擎中。YugabyteDB管理的每一行都存储为DocDB中的一个文档,该文档在内部映射到RocksDB中的多个键值对。

至少可以说,构建一个可大规模扩展、高性能的SQL数据库是一个很难解决的工程问题。使用RocksDB作为一个基础构建块,无疑让我们作为一个工程团队的生活变得更轻松。不用说,我们非常感谢RocksDB社区多年来的杰出工作。

原文地址:https://blog.yugabyte.com/how-we-built-a-high-performance-document-store-on-rocksdb/

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1935.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册