起源

RocksDB我的项目是起源于Facebook,是一款作为各种存储介质上的服务器工作负载的存储引擎,最后专一于疾速存储(尤其是闪存存储)。它是一个 C++ 库,用于存储任意大小的字节流的键和值。它反对点查找和范畴扫描,并提供不同类型的 ACID 保障。
在可定制性和自适应性之间获得均衡。RocksDB 具备高度灵便的配置设置,能够调整为在各种生产环境中运行,包含 SSD、硬盘、ramfs 或近程存储。它反对各种压缩算法和良好的生产反对和调试工具。另一方面,还致力限度旋钮的数量,提供足够好的开箱即用性能,并在实用的中央应用一些自适应算法。

RocksDB 借鉴了开源leveldb我的项目的重要代码以及Apache HBase的想法。最后的代码是从开源 leveldb 1.5 fork进去的。

很多我的项目都接收了RocksDB作为其后端存储的一种解决方案,如Mysql, Ceph, Flink, MongoDB, TiDB等。

我的项目链接:

leveldb: https://github.com/google/lev...

rocksdb: https://github.com/facebook/r...

对于LSM树

LSM树,即日志构造合并树(Log-Structured Merge-Tree)。其实它并不属于一个具体的数据结构,它更多是一种数据结构的设计思维。大多NoSQL数据库核心思想都是基于LSM来做的,只是具体的实现不同。

传统关系型数据库应用btree或一些变体作为存储构造,能高效进行查找。但保留在磁盘中时它也有一个显著的缺点,那就是逻辑上相离很近但物理却可能相隔很远,这就可能造成大量的磁盘随机读写。随机读写比程序读写慢很多,为了晋升IO性能,咱们须要一种能将随机操作变为程序操作的机制,于是便有了LSM树。

从概念上说,最根本的LSM是很简略的 。将之前应用一个大的查找构造(造成随机读写,影响写性能),变换为将写操作程序的保留到一些类似的有序文件(也就是sstable)中。所以每个文件包 含短时间内的一些改变。因为文件是有序的,所以之后查找也会很快。文件是不可批改的,他们永远不会被更新,新的更新操作只会写到新的文件中。读操作查看很 有的文件。通过周期性的合并这些文件来缩小文件个数。

架构

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

RocksDB 的三个根本构造是memtable、sstfile和logfile。memtable是一种内存数据结构 - 新的写入被插入到memtable中,并且能够抉择写入日志文件(又名。Write Ahead Log(WAL))。日志文件是存储上按程序写入的文件。当 memtable 填满时,它会被刷新到存储上的sstfile,并且能够平安地删除相应的日志文件。对 sstfile 中的数据进行排序以不便查找键。

RocksDB应用布隆过滤器来断定键在哪个sst文件中。为了防止随机写,它将数据积攒到内存中的memtable中,而后一次性刷写到硬盘中。RocksDB的文件是不可变的,一旦生成就不会持续写该文件。记录不会被更新或者删除,会生成一个新文件。这会在硬盘生成一些多余的数据,会须要数据库Compaction(压缩),Compaction文件会移除冗余的键值对并腾出空间,如图所示

RocksDB用不同的排列组织数据,也就是层level,每层都有个指标大小,每层的指标大小增长倍数是雷同的,默认是10倍,因而,如果第一层指标大小1g,那么2,3,4层大小就是10g,100g,1000g,一个键可能呈现在不同的层,随着compaction,然而越新的值层越高,越旧的值层越低。

RocskDB和LevelDB的区别

构造和levelDB大同小异,只是多了一些改良

  1. 减少了column family,有了列簇的概念,可把一些相干的key存储在一起
  2. 内存中有多个immute memtalbe,可避免Leveldb中的 write stall(写进展)
  3. 可反对多线程同时compaction,实践上多线程同时compction会比一个线程compaction要快
  4. 反对TTL过期淘汰机制
  5. flush与compation离开不同的线程池来调度,并具备不同的优先级,flush要优于compation,这样能够放慢flush,避免stall
  6. 对SSD存储做了优化,能够以in-memory形式运行
  7. 减少了对 write ahead log(WAL)的管理机制,更方便管理WAL,WAL是binlog文件
  8. 反对多种不同的compaction策略

RocksDB 子模块

RocksDB 5大子模块,别离为:

  1. Basic Operation,基本操作定义
  2. Terminology,外部术语定义
  3. Tool,外部工具
  4. Logging/Monitoring ,日志和监控
  5. System Behavior,外部零碎行为
Basic Operation

除了 RocksDB 外围的KV的操作接口get,put两类操作外,RocksDB 还在此模块中封装了如下几类能实用于非凡应用场景的操作:
Iteration,Rocks DB可能反对区间范畴内的key迭代器的遍历查找。
Compaction Filter,用户可应用 Compaction Filter 对 key 值进行删除或其它更新操作的逻辑定义,当零碎进行 Compact 行为的时候。
Creating and Ingesting SST files,当用户想要疾速导入大批量数据到零碎内时,能够通过线下创立无效格局的 SST 文件并导入的形式,而非应用 API 进行 KV 值的独自PUT操作。
Delete Range,区间范畴的删除操作,比一个个 Key 的独自删除调用应用更不便。
Low Priority Write,当用户执行大批量数据 load 的操作时但放心可能会影响到零碎失常的操作解决时,能够开启此属性进行优先级的调整。
Read-Modify-Write,这个操作的理论含意是 Merge 操作的含意,读取现有键值,进行更新(累加计数或依赖原有值的任何更新操作),将新的值写入到原 Key 下。 如果应用原始 Get/Set API 的前提下,咱们要调用2次 Get 1次,而后再 Set 1次,在 Merge API 下,使用者调用1次就足够了。
Transaction,RocksDB 外部提供乐观式的 OptimisticTransactionDB 和乐观式(事务锁形式)的 TransactionDB 来反对并发的键值更新操作。

Terminology

首先是RocksDB外部的相干术语定义阐明,如上图所示,次要有以下一些术语:

Write-Ahead-Log File,相似于HDFS JournalNode中的editlog,用于记录那些未被胜利提交的数据操作,而后在重启时进行数据的复原。
SST File,SST文件是一段排序好的表文件,它是理论长久化的数据文件。外面的数据依照key进行排序能不便对其进行二分查找。在SST文件内,还额定蕴含以下非凡信息:
Bloom Fileter,用于疾速判断指标查问key是否存在于以后SST文件内。
Index / Partition Index,SST外部数据块索引文件疾速找到数据块的地位。
Memtable,内存数据结构,用以存储最近更新的db更新操作,memtable空间写满后,会触发一次写出更新操作到SST文件的操作。
Block Cache,纯内存存储构造,存储SST文件被常常拜访的热点数据。

System Behavior

在RocksDB外部,有着许多零碎操作行为来保障系统的安稳运行。

Compression,SST文件内的数据可能被压缩存储来减小占用空间。
Rate Limit行为。用户可能对其写操作进行速度管制,以此防止写入速度过快造成零碎读提早的景象。
Delete Schedule,系统文件删除行为的速度管制。
Direct IO,RocksDB反对绕过零碎Page Cache,通过利用内存从存储设置中间接进行IO读写操作。
Compaction,数据的Compact行为,删除SST文件中反复的key以及过期的key数据。

Logging/Monitoring

RocksDB外部有以下的日志监控工具:
Logger,可用的Logger应用类。
Statistic / Perf Context and IO Stats Context,RocksDB外部各类型操作的工夫,操作数计数统计信息,此数据信息能被用户用来发现零碎的性能瓶颈操作。
EventListener,此监听接口提供了一些event事件产生后的接口回调,比方实现一次flush操作,开始Compact操作的时候等等。

联合官网Example剖析代码

int main() {  DB* db;  Options options;  // 优化 Rocksdb 的配置  options.IncreaseParallelism();  options.OptimizeLevelStyleCompaction();  options.create_if_missing = true;  // 关上数据库  Status s = DB::Open(options, kDBPath, &db);  assert(s.ok());  // 操作数据库  s = db->Put(WriteOptions(), "key1", "value");  assert(s.ok());  std::string value;    s = db->Get(ReadOptions(), "key1", &value);  assert(s.ok());  assert(value == "value");  // 原子化操作数据库  {    WriteBatch batch;    batch.Delete("key1");    batch.Put("key2", value);    s = db->Write(WriteOptions(), &batch);  }  s = db->Get(ReadOptions(), "key1", &value);  assert(s.IsNotFound());  db->Get(ReadOptions(), "key2", &value);  assert(value == "value");  // 应用 PinnableSlice 缩小拷贝  {    PinnableSlice pinnable_val;    db->Get(ReadOptions(), db->DefaultColumnFamily(), "key2", &pinnable_val);    assert(pinnable_val == "value");  }  {    std::string string_val;        PinnableSlice pinnable_val(&string_val);    db->Get(ReadOptions(), db->DefaultColumnFamily(), "key2", &pinnable_val);    assert(pinnable_val == "value");    assert(pinnable_val.IsPinned() || string_val == "value");  }  PinnableSlice pinnable_val;  s = db->Get(ReadOptions(), db->DefaultColumnFamily(), "key1", &pinnable_val);  assert(s.IsNotFound());  pinnable_val.Reset();  db->Get(ReadOptions(), db->DefaultColumnFamily(), "key2", &pinnable_val);  assert(pinnable_val == "value");  pinnable_val.Reset();  delete db;  return 0;}

更多技术分享浏览我的博客:

https://thierryzhou.github.io

本文由mdnice多平台公布