RocksDB 概览

一、介绍

RocksDB 在Facebook开始是作为各种存储介质上服务器工作负载的存储引擎,最初专注于快速存储(尤其是闪存存储)。它是一个 C++ 库,用于存储键和值,它们是任意大小的字节流。它支持点查找和范围扫描,并提供不同类型的 ACID 保证。

在可定制性和自适应性之间取得平衡。RocksDB 具有高度灵活的配置设置,可以进行调整以在各种生产环境中运行,包括 SSD、硬盘、ramfs 或远程存储。它支持各种压缩算法和生产支持和调试的好工具。另一方面,还努力限制旋钮的数量,提供足够好的开箱即用性能,并在适用的情况下使用一些自适应算法。

RocksDB 借鉴了开源leveldb项目的重要代码以及来自Apache HBase 的想法。最初的代码是从开源 leveldb 1.5 分叉出来的。它还建立在 Facebook 在 RocksDB 之前开发的代码和想法之上。

2. 假设和目标

表现:
RocksDB 的主要设计点是它应该对快速存储和服务器工作负载具有高性能。它应该支持有效的点查找以及范围扫描。它应该是可配置的,以支持高随机读取工作负载、高更新工作负载或两者的组合。其架构应支持轻松调整不同工作负载和硬件的权衡。

生产支持:
RocksDB 应该以这样一种方式设计,即它具有对有助于在生产环境中部署和调试的工具和实用程序的内置支持。如果存储引擎还不能自动适应应用程序和硬件,我们会提供一些参数让用户调整性能。

兼容性:
该软件的较新版本应向后兼容,以便在升级到较新版本的 RocksDB 时无需更改现有应用程序。除非使用新提供的功能,否则现有应用程序也应该能够恢复到最近的旧版本。请参阅不同版本之间的 RocksDB 兼容性。

3. 高层架构

RocksDB 是一个键值存储接口的存储引擎库,其中键和值是任意字节流。RocksDB组织所有数据的排序顺序和常用操作Get(key),NewIterator(),Put(key, val),Delete(key),和SingleDelete(key)。

RocksDB 的三个基本结构是memtable、sstfile和logfile。所述的memTable是一个存储器内数据结构-新的写入被插入的memTable和任选地写入到日志文件(又名写日志(WAL)。) 。日志文件是存储上按顺序写入的文件。当 memtable 填满时,它会被刷新到存储上的sstfile并且可以安全地删除相应的日志文件。sstfile 中的数据已排序,以便于轻松查找键。

sstfile 的默认格式在这里有更详细的描述。

4. 特点

列族
RocksDB 支持将一个数据库实例划分为多个列族。所有数据库都使用名为“default”的列族创建,用于未指定列族的操作。

RocksDB 保证用户跨列族的一致视图,包括在启用 WAL 或启用原子刷新时的崩溃恢复之后。它还通过WriteBatchAPI支持原子跨列族操作。

更新
甲PutAPI插入一个单一的键-值对的数据库。如果该键已存在于数据库中,则先前的值将被覆盖。甲WriteAPI允许多个密钥值以原子方式插入,更新,或在数据库中删除。数据库保证单个Write调用中的所有键值都将插入到数据库中,或者它们都不会插入到数据库中。如果这些键中的任何一个已经存在于数据库中,则先前的值将被覆盖。DeleteRangeAPI 可用于删除范围内的所有键。

获取、迭代器和快照
键和值被视为纯字节流。键或值的大小没有限制。该GetAPI允许应用程序从数据库中获取单个密钥值。该MultiGetAPI允许应用程序取回一串从数据库密钥。通过MultiGet调用返回的所有键值都是相互一致的。

数据库中的所有数据都是按逻辑顺序排列的。应用程序可以指定指定键的总排序的键比较方法。一个IteratorAPI允许应用程序做数据库的范围扫描。该Iterator可以寻求到指定的键,然后应用程序可以在开始从该点的时间扫描一个关键。该IteratorAPI还可以用来做数据库中键的逆序迭代。创建时Iterator会创建数据库的一致时间点视图。因此,通过 返回的所有键Iterator都来自数据库的一致视图。

甲SnapshotAPI允许应用程序创建一个数据库的一个点在时间图。的Get和IteratorAPI可用于读取从指定的快照数据。从某种意义上说,aSnapshot和 anIterator都提供了数据库的时间点视图,但它们的实现不同。短期/前景扫描最好通过迭代器完成,而长期运行/后台扫描最好通过快照完成。AnIterator保持对与数据库的该时间点视图相对应的所有底层文件的引用计数 - 这些文件在Iterator发布之前不会被删除。A Snapshot,另一方面,不会阻止文件删除;相反,压实过程理解存在Snapshots并承诺永远不会删除在任何现有Snapshot.

Snapshots在数据库重启后不会持久化:重新加载 RocksDB 库(通过服务器重启)会释放所有预先存在的Snapshots.

交易
RocksDB 支持多操作事务。它支持乐观和悲观两种模式。请参阅交易。

前缀迭代器
大多数 LSM 树引擎无法支持高效的范围扫描 API,因为它需要查看多个数据文件。但是,大多数应用程序不会对数据库中的键范围进行纯随机扫描;相反,应用程序通常在密钥前缀内进行扫描。RocksDB 充分利用了这一点。应用程序可以配置 aOptions.prefix_extractor以启用基于键前缀的过滤。当Options.prefix_extractor设置,前缀的哈希值也被添加到布鲁姆。一个Iterator指定的键前缀(中ReadOptions)将使用布隆过滤器,以避免寻找到不包含与指定键前缀密钥的数据文件。请参阅前缀查找。

坚持
RocksDB 有一个预写日志(WAL)。所有写操作(Put,Delete和Merge)都存储在称为 memtable 的内存缓冲区中,并且可以选择插入到 WAL 中。重新启动时,它会重新处理日志中记录的所有事务。

WAL 可以配置为存储在与 SST 文件存储目录不同的目录中。这对于您可能希望将所有数据文件存储在非持久性快速存储中的情况是必要的。同时,您可以通过将所有事务日志放在较慢但持久的存储上来确保不会丢失数据。

每个Put都有一个标志,通过 设置WriteOptions,它指定是否Put应该将 插入到事务日志中。该WriteOptions还可以指定是否fsync调用时事务日志之前Put被宣告犯。

在内部,RocksDB 使用批处理提交机制将事务批处理到日志中,以便它可以使用单个fsync调用提交多个事务。

数据校验
RocksDB 使用校验和来检测存储中的损坏。这些校验和适用于每个 SST 文件块(通常在4K到之间128K)。块一旦写入存储,就永远不会被修改。RocksDB 还维护一个完整的文件校验和。

RocksDB 动态检测并利用 CPU 校验和卸载支持。

多线程压缩
在存在持续写入的情况下,需要压缩以提高空间效率、读取(查询)效率和及时删除数据。压缩删除已删除或覆盖的键值绑定,并重新组织数据以提高查询效率。如果配置,压缩可能会在多个线程中发生。

整个数据库存储在一组sstfiles 中。当一个memtable已满时,它的内容被写出到 LSM 树的 Level-0 (L0) 中的文件中。当内存表刷新到 L0 中的文件时,RocksDB 会删除内存表中重复和覆盖的键。在compaction 中,一些文件被定期读入并合并以形成更大的文件,通常会进入下一个 LSM 级别(例如 L1,直到 Lmax)。

LSM 数据库的整体写入吞吐量直接取决于压缩发生的速度,尤其是当数据存储在 SSD 或 RAM 等快速存储中时。RocksDB 可以配置为从多个线程发出并发压缩请求。据观察,与单线程压缩相比,当数据库位于 SSD 上时,使用多线程压缩的持续写入速率可能增加 10 倍。

压实样式
级别样式压缩和通用样式压缩都将数据存储在数据库中固定数量的逻辑级别中。较新的数据存储在级别 0 (L0) 中,较旧的数据存储在编号较高的级别中,最高可达 Lmax。L0 中的文件可能有重叠的键,但其他级别中的文件通常在每个级别形成单个排序运行。

级别样式压缩(默认)通常通过最小化每个压缩步骤中涉及的文件来优化磁盘占用空间与逻辑数据库大小(空间放大):将 Ln 中的一个文件与其在 Ln+1 中的所有重叠文件合并,并用新文件替换它们Ln+1。

通用样式压缩通常通过一次合并潜在的许多文件和级别来优化写入磁盘的总字节数与逻辑数据库大小(写入放大),这需要更多的临时空间。与 Level Style Compaction 相比,Universal 通常会导致较低的写入放大率但更高的空间和读取放大率。

FIFO 样式压缩在过时时删除最旧的文件,并可用于类似缓存的数据。在FIFO压缩中,所有文件都在0级。当数据的总大小超过配置大小(CompactionOptionsFIFO::max_table_files_size)时,我们删除最旧的表文件。

我们还使开发人员能够开发和试验自定义压缩策略。出于这个原因,RocksDB 有适当的钩子来关闭内置的压缩​​算法,并有其他 API 允许应用程序运行自己的压缩算法。 Options.disable_auto_compaction,如果设置,则禁用本机压缩算法。该GetLiveFilesMetaDataAPI允许外部组件来看待数据库中的每个数据文件和数据文件,决定合并和紧凑。调用CompactFiles您想要的压缩文件。该DeleteFileAPI允许应用程序被认为过时的删除数据文件。

元数据存储
清单日志文件用于记录所有数据库状态更改。压缩过程添加新文件并从数据库中删除现有文件,并通过将这些操作记录在MANIFEST中使这些操作持久化。

避免摊位
后台压缩线程还用于将内存表内容刷新到存储文件中。如果所有后台压缩线程都忙于执行长时间运行的压缩,那么突然的写入会很快填满内存表,从而导致新的写入停止。这种情况可以通过配置 RocksDB 保留一小组线程来避免,这些线程显式保留用于将memtable刷新到存储的唯一目的。

压实过滤器
某些应用程序可能希望在压缩时处理密钥。例如,具有固有支持生存时间 (TTL) 的数据库可能会删除过期的密钥。这可以通过应用程序定义的Compaction-Filter来完成。如果应用程序想要连续删除超过特定时间的数据,它可以使用压缩过滤器删除已过期的记录。RocksDB 压缩过滤器将控制权交给应用程序来修改键的值或完全删除键作为压缩过程的一部分。例如,作为压缩的一部分,应用程序可以连续运行数据清理器。

只读模式
数据库可以以只读模式打开,在这种模式下,数据库保证应用程序不会修改数据库中的任何内容。这会导致更高的读取性能,因为经常遍历的代码路径完全避免了锁定。

数据库调试日志
默认情况下,RocksDB 将详细日志写入名为 LOG*.txt 的文件中。这些主要用于调试和分析正在运行的系统。用户可以选择不同的日志级别。该日志可以配置为以指定的周期滚动。日志记录接口是可插拔的。用户可以插入不同的记录器。请参阅记录器。

数据压缩
RocksDB 支持 lz4、zstd、snappy、zlib 和 lz4_hc 压缩,以及 Windows 下的 xpress。RocksDB 可以配置为在最底层90%(数据所在的位置)支持不同的数据压缩算法。典型安装可能会为最底层配置 ZSTD(或 Zlib,如果不可用),为其他级别配置 LZ4(或 Snappy,如果不可用)。请参阅压缩。

完整备份和复制
RocksDB提供了一个备份API, BackupEngine。您可以在此处阅读更多相关信息:如何备份 RocksDB

RocksDB 本身不是一个可复制的,但它提供了一些辅助功能,使用户可以在 RocksDB 之上实现他们的复制系统,参见复制助手。

在同一进程中支持多个嵌入式数据库
RocksDB 的一个常见用例是应用程序固有地将其数据集划分为逻辑分区或分片。这种技术有利于应用程序负载平衡和故障快速恢复。这意味着单个服务器进程应该能够同时操作多个 RocksDB 数据库。这是通过名为 的环境对象完成的Env。除其他外,线程池与Env. 如果应用程序想要在多个数据库实例之间共享一个公共线程池(用于后台压缩),那么它应该使用相同的Env对象来打开这些数据库。

同样,多个数据库实例可能共享同一个块缓存或速率限制器。

块缓存——压缩和未压缩的数据
RocksDB 使用LRU 缓存为块提供读取服务。块缓存分为两个单独的缓存:第一个缓存未压缩的块,第二个缓存 RAM 中的压缩块。如果配置了压缩块缓存,用户可能希望启用直接 I/O 以防止 OS 页面缓存中相同数据的冗余缓存。

表缓存
表缓存是一种缓存打开的文件描述符的结构。这些文件描述符用于sstfiles。应用程序可以指定 Table Cache 的最大大小,或者配置 RocksDB 始终保持所有文件打开,以获得更好的性能。

输入/输出控制
RocksDB 允许用户以不同的方式配置来自和到 SST 文件的 I/O。用户可以启用直接 I/O,以便 RocksDB 完全控制 I/O 和缓存。另一种方法是利用一些选项来允许用户提示应该如何执行 I/O。他们可以建议 RocksDB 调用fadvise文件来读取,在被附加的文件中调用定期范围同步,启用直接 I/O 等…更多细节见IO。

可堆叠数据库
RocksDB 有一个内置的包装器机制,可以将功能添加为代码数据库内核之上的一层。此功能由StackableDBAPI封装。例如,生存时间功能是由一个实现的StackableDB,而不是核心 RocksDB API 的一部分。这种方法使代码保持模块化和整洁。

内存表:
可插拔内存表:
RocksDB 的 memtable 的默认实现是一个跳过列表。跳过列表是一个排序集,当工作负载将写入与范围扫描交错时,这是一个必要的构造。然而,一些应用程序不交错写入和扫描,而一些应用程序根本不进行范围扫描。对于这些应用程序,排序集可能无法提供最佳性能。为此,RocksDB 的 memtable 是可插拔的。提供了一些替代实现。三个 memtable 是库的一部分:一个skiplist memtable、一个vector memtable 和一个prefix-hash memtable。向量内存表适用于将数据批量加载到数据库中。每次写入都会在向量的末尾插入一个新元素;当需要刷新内存表以存储向量中的元素时,它们会被排序并写出到 L0 中的文件中。前缀哈希内存表允许有效处理 get、put 和 scans-within-a-key-prefix。尽管 memtable 的可插入性不是作为公共 API 提供的,但应用程序可以在私有分支中提供自己的 memtable 实现。

内存流水线
RocksDB 支持为数据库配置任意数量的内存表。当一个 memtable 已满时,它会变成一个不可变的 memtable,并且后台线程开始将其内容刷新到存储中。同时,新的写入继续累积到新分配的内存表中。如果新分配的 memtable 被填充到其限制,它也会转换为不可变的 memtable 并插入到刷新管道中。后台线程继续将所有管道化的不可变内存表刷新到存储中。这种流水线增加了 RocksDB 的写入吞吐量,特别是当它在慢速存储设备上运行时。

Memtable Flush 期间的垃圾收集:
当内存表被刷新到存储时,将执行内联压缩过程。垃圾的清除方式与压缩相同。从输出流中删除相同密钥的重复更新。类似地,如果较早的 put 被稍后的 delete 隐藏,则 put 根本不会写入输出文件。对于某些工作负载,此功能大大减少了存储和写入放大上的数据大小。

合并运算符
RocksDB 原生支持三种记录类型,一条Put记录,一条Delete记录,一条Merge记录。当压缩过程遇到 Merge 记录时,它会调用一个应用程序指定的方法,称为 Merge Operator。Merge 可以将多个 Put 和 Merge 记录合并为一个。这个强大的功能允许通常执行读取-修改-写入的应用程序完全避免读取。它允许应用程序将操作意图记录为合并记录,并且 RocksDB 压缩过程懒惰地将该意图应用于原始值。此功能在Merge Operator中有详细描述

数据库编号
在创建数据库时创建的全局唯一 ID,默认存储在 DB 文件夹中的 IDENTITY 文件中。或者,它只能存储在 MANIFEST 文件中。建议存储在 MANIFEST 文件中。

5. 工具

有许多有趣的工具可用于支持生产中的数据库。该sst_dump实用程序转储 sst 文件中的所有键值以及其他信息。该ldb工具可以放入、获取、扫描数据库的内容。ldb还可以转储 的内容,MANIFEST也可以用于更改数据库的配置级别数。有关详细信息,请参阅管理和数据访问工具。

6. 测试

有一堆单元测试可以测试数据库的特定功能。一个make check命令运行所有的单元测试。单元测试会触发 RocksDB 的特定功能,并非旨在大规模测试数据的正确性。该db_stress测试用于大规模验证数据的正确性。请参阅压力测试。

7. 性能

RocksDB 的性能是通过一个名为db_bench. db_bench是 RocksDB 源代码的一部分。此处描述了一些使用闪存存储的典型工作负载的性能结果。您还可以在此处找到针对内存工作负载的 RocksDB 性能结果。

参考:https://github.com/facebook/rocksdb/wiki/RocksDB-Overview

你可能感兴趣的:(数据库,RocksDB初步学习,RocksDB,概览,RocksDB,基本概念解析)