Overview

Introduction

RocksDB的项目起源于Facebook的一个实验,希望能够开发一个高效的数据库实现能够在快速存储设备(特别是Flash)上存储数据并服务服务器的负载,同时完全挖掘这类存储设备的潜能。
RocksDB是一个C++库用于存储kv数据并且支持原子读写。
RocksDB实现了在配置上的较高的灵活性并且可以运行到各种生产环境中,包括纯内存、Flash、HDD或者HDFS。RocksDB支持多种压缩算法以及多种工具用于生产支持以及debug。
RocksDB借用了许多LevelDB的代码以及Apache HBase中的思想。最初是基于LevelDB1.5开发。

Assumptions and Goals

Performance

RocksDB的主要设计点是它应该具有快速存储和服务器工作负载的性能。他需要能够利用由Flash或者RAM提供的高读写性能,支持高效的单点查找以及范围查找,支持高随机读负载或者高update负载或者两者的混合负载。其结构需要支持能够方便地进行读写空间利用放大的调整。

Production Support

RocksDB的设计应该能够内置对工具和实用程序的支持,以帮助在生产环境中进行部署和调试。 大多数主要参数应该是完全可调的,以便它可以被不同硬件上的不同应用程序使用。

Compatibility

此软件的较新版本应向后兼容,以便在升级到RocksDB的较新版本时不需要更改现有应用程序。

High Level Architecture

RocksDB是一个嵌入式键值存储,其中键和值是任意字节流。 RocksDB按排序顺序组织所有数据,常见操作是Get(key),Put(key),Delete(key)和NewIterator()
RocksDB的三个基本的结构是memtablesstfile以及logfilememtable是一个in-memory数据结构,新的写都被插入memtable以及可选地插入logfilelogfile是一个在存储设备上的顺序写的文件,当memtable满了之后会被刷写到存储设备上的sstfile,同时对应的logfile能够被安全地删除,sstfile中的数据是有序的。

Features

Column Family

RocksDB支持将一个数据库实例划分成多个column family,初始化的时候会创建一个default的column family,用于没有指定具体的column family的操作
RocksDB为用户保证了跨column family的一致性,包括在崩溃恢复之后(WAL允许),支持跨column family的原子操作(WriteBatch)

Update

Put API将单个KV插入数据库。 如果key已存在于数据库中,则将覆盖先前的值。 Write API允许将多个kv原子地插入到数据库中。 数据库保证要么将单个Write调用中的所有kv插入到数据库中,要么不将任何kv插入到数据库中。 如果数据库中已存在任何这些key,则将覆盖先前的value。

Get、Iterator and snapshot

key和value被视为纯字节流。 key或者value的大小没有限制。 Get API允许应用程序从数据库中获取单个kv。 MultiGet API允许应用程序从数据库中检索多个kv。 通过MultiGet调用返回的所有kv都是彼此一致的。

数据库中的所有数据按排序顺序进行逻辑排列。 应用程序可以指定key的比较方法,该方法指定key的总排序。 Iterator API允许应用程序在数据库上执行RangeScan。 迭代器可以寻找指定的key,然后应用程序可以从该点开始一次扫描一个key。 Iterator API还可用于对数据库中的key进行反向迭代。 创建Iterator时会创建数据库的一致时间点视图。 因此,通过迭代器返回的所有key都来自数据库的一致视图。

Snapshot API允许应用程序创建数据库的时间点视图。 Get和Iterator API可用于从指定的快照读取数据。 从某种意义上说,Snapshot和Iterator都提供了数据库的时间点视图,但它们的实现是不同的。 短期/前台扫描最好通过迭代器完成,而长时间运行/后台扫描最好通过快照完成。 迭代器会在与数据库的时间点视图对应的所有基础文件上保留引用计数 - 这些文件在Iterator发布之前不会被删除。 另一方面,快照不会阻止文件删除; 相反,compaction过程知道快照的存在,并且承诺永远不会删除任何现有快照中可见的key。

Snapshot不能在数据库重启的时候持久化,也就是说重启RocksDB的时候会释放所有的snapshot

Transactions

RocksDB支持多操作事务。 它支持乐观和悲观模式。

Prefix Iterators

大多数LSM引擎不支持高效的RangeScan API,因为它需要查看每个data file。 但是大多数应用程序不对数据库中的key range进行纯随机扫描; 相反,应用程序通常在key前缀内扫描。 RocksDB利用了这个特点。 应用程序可以配置prefix_extractor以指定key前缀。 RocksDB使用它来存储每个key前缀的bloom(filter?)。 指定前缀(通过ReadOptions)的迭代器将使用这些bloom位来避免查看不包含具有指定key前缀的key的数据文件。

Persistence

RocksDB有一个事务日志。所有Puts都存储到memtable,并可选择插入到事务日志中。重新启动时,它会重新处理事务日志中记录的所有事务。

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

每个Put都有一个标志,通过WriteOptions设置,它指定Put是否应该插入到事务日志中。 WriteOptions还可以指定在声明Put被提交之前是否向事务日志发出同步调用。

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

Fault Tolerance

RocksDB使用校验和来检测存储中的损坏。 这些校验和适用于每个SST文件块(通常在4K到128K之间)。 一旦写入存储,块就永远不会被修改。 RocksDB动态检测硬件支持校验和计算,并在可用时利用该支持。

Multi-Threaded Compactions

如果应用程序覆盖现有key,则需要进行compaction以删除可能出现的相同key的多个副本。compaction也处理key的删除。如果配置适当,可能会在多个线程中发生compaction。

整个数据库存储在一组sstfiles中。当memtable已满时,其内容将被写入Level-0(L0)中的文件。当RocksDB flush 到L0中的文件时,RocksDB会删除memtable中的重复和被覆写的key。有些文件会定期读入并合并以形成更大的文件 - 这称为compaction

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

Compaction Style

Universal Style Compaction操作完全排序的sst,这些sst是L0文件或L1 +的整个级别。compaction会选择一些按时间顺序相邻的已排序sst,并将它们合并回一个新的sst。

Level Style Compaction将数据存储在数据库中的多个level。最近的数据存储在L0中,最旧的数据存储在Lmax中。 L0中的文件可能具有重叠的key,但其他层中的文件则没有。compaction过程选择Ln中的一个文件及其在Ln + 1中的所有重叠文件,并用Ln + 1中的新文件替换它们。Universal Style Compaction通常有较低的写放大,但比Level Style Compaction具有更高的空间和读放大。

FIFOStyle Compaction在过时的时候删除最旧的文件,可用于类缓存数据。

我们还使开发人员能够开发和试验自定义compaction策略。出于这个原因,RocksDB有适当的hook来关闭内置的compaction算法,并有其他API允许应用程序操作自己的compaction算法。如果设置了Options.disable_auto_compaction,则禁用本机compaction算法。 GetLiveFilesMetaData API允许外部组件查看数据库中的每个数据文件,并决定合并和compaction哪些数据文件。调用CompactFiles来compact所需的文件。 DeleteFile API允许应用程序删除被视为过时的数据文件。

Metadata Storage

数据库中的MANIFEST文件记录数据库状态。 compaction过程添加新文件并从数据库中删除现有文件,并通过将它们记录在MANIFEST文件中来使这些操作持久化。 要记录在MANIFEST文件中的事务使用批量提交算法来分摊重复同步到MANIFEST文件的成本。

Avoding Stalls

后台compaction线程还用于将可memory的数据刷新到存储设备上的文件。 如果所有后台compaction线程都在忙于执行长时间运行的compaction,那么突发的大量写操作可以快速填满memtable,从而使得写操作被阻塞。 通过将RocksDB配置一组保留线程专门用于将memtable flush到存储设备,可以避免这种情况。

Compaction Filter

某些应用程序可能希望在compaction时处理key。 例如,具有对生存时间(TTL)的固有支持的数据库可以移除过期的key。 这可以通过应用程序定义的compaction filter完成。 如果应用程序想要连续删除早于特定时间的数据,则可以使用compaction filter删除已过期的记录。 RocksDB的compaction filter控制应用程序来修改key的值或完全删除key作为compaction过程的一部分。 例如,应用程序可以连续运行数据清理程序作为compaction的一部分。

ReadOnly Mode

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

Database Debug Logs

RocksDB将详细的log写到了LOG* 文件中,主要被用于debug或者分析运行系统。

Data Compression

RocksDB支持snappy,zlib,bzip2,lz4,lz4_hc和zstd压缩。 RocksDB可以为不同level配置不同压缩算法。 通常,Lmax中有90%的数据。一般可以为最底层配置ZSTD,为其它层配置LZ4压缩(如果不可用则为Snappy))。

Full Backup、Incremental Backup and Replication

RocksDB支持增量备份。 BackupableDB使RocksDB的备份变得简单。

增量复制需要能够查找并将数据库的所有最新更改记录到最后。 API GetUpdatesSince允许应用程序RocksDB事务日志记录到最后。它可以连续从RocksDB事务日志中获取事务,并将它们应用于远程副本或远程备份。

复制系统通常希望使用一些任意元数据来注释每个Put。此元数据可用于检测复制流水线中的循环。它还可用于对事务进行时间戳和顺序事务。为此,RocksDB支持一个名为PutLogData的API,应用程序可以使用该API来为每个Put添加元数据。此元数据仅存储在事务日志中,不存储在数据文件中。可以通过GetUpdatesSince API检索通过PutLogData插入的元数据。

RocksDB事务日志在数据库目录中创建。当不再需要日志文件时,它将移动到存档目录。存档目录存在的原因是因为落后的复制流可能需要从过去的日志文件中检索事务。 API GetSortedWalFiles返回所有事务日志文件的列表。

Support for Multiple Embedded Database in the same process

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

类似地,多个数据库实例可以共享相同的block cache。

Block Cache——Compressed and Uncompressed Data

RocksDB使用LRU缓存来为加速读取。 块缓存被分为两个独立的缓存:第一个缓存未压缩的块,第二个缓存压缩块。 如果配置了压缩块缓存,用户可能希望启用直接I / O以防止OS页面缓存双重缓存相同的压缩数据。

Table Cache

用于缓存打开的sst文件的描述符,cache容量可以自行配置

I/O control

RocksDB有一些选项允许用户提示应该如何执行I / O. 他们可以向RocksDB提出建议调用文件中的fadvise来读取,为被追加的文件调用周期性的range同步,或启用直接I / O.

Stackable RocksDB

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

Memtable

可插拔的Memtables:

RocksDB的memtable的默认实现是skiplist。 skiplist是一个有序集合,当工作负载交错使用range scan进行写入时,这就是一个必要的结构。但是,某些应用程序不会交替执行write和scan,并且某些应用程序根本不进行range scan。对于这些应用程序,排序集可能无法提供最佳性能。出于这个原因,RocksDB支持可插拔的API,允许应用程序提供自己的memtable实现。RocksDB提供三个memtables的默认实现:skiplist memtable,vector memtable和prefix-hash memtable。vector memtable适用于将数据批量加载到数据库中。每次写入都会在vector的末尾插入一个新元素;当需要将memtable flush到存储设备时,vector中的元素会被排序并写入L0中的文件。prefix-hash memtable能够有效处理get,puts和scan-in-a-key-prefix。

memtable pipeline

RocksDB支持为数据库配置任意数量的memtables。当memtable满了,它变成一个immutable memtable,后台线程开始将其内容flush 到设备。同时,新数据持续写到新分配的memtable。如果新分配的memtable写满,它也会转换为immutable memtable并插入到flush pipeline中。后台线程继续将所有immutable memtables刷新到存储。这种流水线操作增加了RocksDB的写入吞吐量,特别是当它在慢速存储设备上运行时。

Memtable Flush期间的GC:

当memtable被刷新到存储时,一个内联压缩(inline-compaction)过程会从输出流中删除重复记录。类似地,如果先前的put被后来的delete隐藏,则put根本不会写入输出文件。此功能大大减少了存储放大以及写放大。当RocksDB用作producer-consumer-queue时,这是一个基本特征,尤其是当队列中元素的生命周期非常短暂时。

**Merge **

RocksDB本身支持三种类型的记录,Put,Delete和Merge。 当compaction过程遇到Merge时,它会调用一个名为Merge Operator的应用程序指定方法。 merge可以将多个Put和Merge合并为一个。 这个强大的功能允许通常执行read-modify-write的应用程序完全避免读取。 它允许应用程序将操作意图记录为merge记录,而RocksDB compaction过程则lazily将该意图应用于value上。

Tools

RocksDB提供许多工具用于支持生产中的数据库。 sst_dump程序可以将所有的kv数据dump到一个sst。 ldb工具可以put,get,scan数据库的内容。 ldb也可以dump MANIFEST的内容,它也可以用来改变数据库配置的level数。 也可用于手动compact数据库。

Tests

有许多单元测试可以测试数据库的特定功能。 make check命令运行所有单元测试。 单元测试触发RocksDB的特定功能,并不是为了大规模测试数据的正确性而设计的。 db_stress测试用于大规模验证数据正确性。

Performance

RocksDB性能通过名为db_bench的实用程序进行基准测试。 db_bench是RocksDB源代码的一部分。

https://rocksdb.org.cn/doc/RocksDB-Basics.html

你可能感兴趣的:(Overview)