关于后端:Rocksdb简介

1次阅读

共计 4919 个字符,预计需要花费 13 分钟才能阅读完成。

起源

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 多平台公布

正文完
 0