前言

古话说得好:“工欲善其事必先利其器”,要做好一件事情之前先把工具或者武器强化一下还是很值当的。所以本文将会把RocksDB的主要概念向大家讲解一下,方便后面具体内容的展开。本文所提到的概念大家仅需要了解和留个印象,如果不是很理解的话不需要纠结,后续的章节中会详细展开。

正文

RocksDB的概念纷繁复杂,我根据自己的理解将概念分为架构概念、存储概念以及操作概念,分门别类,帮助大家理解。下面就按照这个观其貌、识其物、辩其行的顺序进行讲解。

架构概念

嵌入式数据库

首先RocksDB是一个嵌入式数据库,这个根本特征决定了RocksDB不需要单独部署,也不需要单独的进程,随应用程序一起部署即可,节省资源且便于管理。

另外由于RocksDB未实现真正意义上的分布式(虽然底层可以使用HDFS目录进行存储,但是数据未作分片,所以无法实现真正的分布式),准确来说是一个单机的KV数据库。

但是没有分布式的血统不代表没有一颗成为分布式应用的心。在实际的分布式使用场景中以RocksDB作为某个副本的存储介质,上层通过Paxos或者Raft协议来保证副本之间的数据一致性,完美的明确了RocksDB在分布式应用使用场景中的定位与作用。

LSM

这是RocksDB的LSM树模型,具体的LSM树相关的概念和知识请参考之前的博文俗话说别在一棵树上吊死,那为什么那么多NOSQL都喜欢在LSM树上吊死呢?

根据这个模型,数据的写入流程为:

  • 任何的写入都会先写到 WAL,然后在写入 Memory Table(Memtable)。当然为了性能,也可以不写入 WAL,但这样就可能面临崩溃丢失数据的风险。Memory Table 通常是一个能支持并发写入的 skiplist,但 RocksDB 同样也支持多种不同的 Memory Table,下面在Memory Table章节详细讲解,用户可以根据实际的业务场景进行选择,如果没有特殊需求,默认即可。

  • 当一个 Memory Table 写满了之后,就会变成 immutable 的 Memory Table,RocksDB 在后台会通过一个 flush 线程根据条件从flush queue中按顺序将 Memory Table flush 到磁盘,生成一个 Sorted String Table(SST) 文件,放在 Level 0 层。当 Level 0 层的 SST 文件个数超过阈值之后,就会通过 Compaction 策略将其放到 Level 1 层,以此类推,直到最底层

Column Family

这是从HBase老师那里学来的,HBase老师就有Column Family的概念,严格上来说HBase是宽列式数据库,列族级别是列式存储,列族内部是行式存储。

由于RocksDB是KV数据库,没有schema的概念,所有数据都是二进制,采用列式存储。不同的Column Family共享WAL,独享sst和memtable,所以Column Family起到了一定的逻辑和资源隔离的作用。

RocksDB的每个键值对都与唯一一个列族(column family)结合。如果没有指定Column Family,键值对将会结合到“default” 列族。

通过共享WAL文件,RocksDB实现了原子写。通过隔离memtable和table文件,RocksDB可以独立配置每个列族并且快速删除它们。

TransactionDB/OptimisticTransactionDB

这个RocksDB两个不同的事务数据库模式,可以理解为悲观事务数据库和乐观事务数据库。对应的是关系型数据库的悲观锁和乐观锁,所以这个应该很好理解,下面来看看两者的具体说明:

  • TransactionDB:简单来说就是在所有写入的时候进行加锁判断,如果无法加锁,则当前数据正在被操作,需要等待;如果加锁成功,则可以在直接操作数据。该类型的事务数据库适用于高并发场景下数据被频繁修改的场景。

  • OptimisticTransactionDB:简单来说就是在写入的数据不使用任何锁,而在提交的时候判断事务是否被修改。如果事务被修改而造成冲突,提交会返回错误,需要重新处理。显而易见,如果频繁修改一条数据,则重新处理的成本过高,所以,该类型数据库适用于大量非事务写入,少量事务写入的场景。

Options

RocksDB的配置项,在实例化RocksDB时作为构造函数传给构造对象。java示例如下:

String dbPath = "/Users/bangcle/Documents/rocksdb/data";
Options options = new Options();
RocksDB rocksDB = RocksDB.open(options, dbPath);

RocksDB有非常多的配置,但是对于多数用户来说,很多选项都是可以不管的,因为他们里面的大多数,都只会影响特定的工作负荷。通常,大多数rocksDB的选项只要保持默认就好了。

下面这些选项,可以帮助我们在大多数情况获得一个合理的开箱即用的性能。建议用户在使用新的rocksdb工程的时候使用以下选项:

cf_options.level_compaction_dynamic_level_bytes = true;
options.max_background_compactions = 4;
options.max_background_flushes = 2;
options.bytes_per_sync = 1048576;
options.compaction_pri = kMinOverlappingRatio;
table_options.block_size = 16 * 1024;
table_options.cache_index_and_filter_blocks = true;
table_options.pin_l0_filter_and_index_blocks_in_cache = true;

如果你有服务使用默认选项运行,而不是使用这些设置,也不太过担心。尽管这些选项比默认选项好,但是它们一般也不会带来明显的性能优化。建议先按照默认配置进行数据处理,如果性能有瓶颈,可以根据需求进行优化,具体的优化措施会在性能优化的章节详细讲解。

存储概念

Memory Table

MemTable是一个内存数据结构,他保存了落盘到SST文件前的数据。他同时服务于读和写:

  • 新的写入总是将数据插入到memtable

  • 读取在查询SST文件前总是要查询memtable,因为memtable里面的数据总是更新的。

一旦一个memtable被写满,他会变成不可修改的,并被一个新的memtable替换。一个后台线程会把这个memtable的内容落盘到一个SST文件,然后这个memtable就可以被销毁了。

影响memtable的最重要的几个选项是:

  • memtable_factory: memtable对象的工厂。通过声明一个工厂对象,用户可以改变底层memtable的实现,并提供事先声明的选项。

  • write_buffer_size:一个memtable的大小

  • db_write_buffer_size:多个列族的memtable的大小总和。这可以用来管理memtable使用的总内存数。

  • write_buffer_manager:除了声明memtable的总大小,用户还可以提供他们自己的写缓冲区管理器,用来控制总体的memtable使用量。这个选项会覆盖db_write_buffer_size

  • max_write_buffer_number:内存中可以拥有刷盘到SST文件前的最大memtable数。

默认的memtable实现是基于skiplist的。除了默认的memtable实现,用户可以使用其他memtable实现,例如HashLinkList,HashSkipList或者Vector,以加快查询速度。下面列出各个MEMTABLE类型的对比表:

MEMTABLE类型 SKIPLIST HASHSKIPLIST HASHLINKLIST VECTOR
最佳使用场景 通用 带特殊key前缀的范围查询 带特殊key前缀,并且每个前缀都只有很小数量的行 大量随机写压力
索引类型 二分搜索 哈希+二分搜索 哈希+线性搜索 线性搜索
是否支持全量db有序扫描 天然支持 非常耗费资源(拷贝以及排序生成一个临时视图 同HashSkipList 同HashSkipList
额外内存 平均(每个节点有多个指针 高(哈希桶+非空桶的skiplist元数据+每个节点多个指针 稍低(哈希桶+每个节点的指针 低(vector尾部预分配的内存)
Memtable落盘 快速,以及固定数量的额外内存 慢,并且大量临时内存使用 同HashSkipList 同HashSkipList
并发插入 支持 不支持 不支持 不支持
带Hint插入 支持(在没有并发插入的时候 不支持 不支持 不支持

Block Cache

Block Cache是Rocksdb在内存中缓存数据以用于读取的地方。用户可以带上一个期望的空间大小,传一个Cache对象给RocksDB实例。一个缓存对象可以在同一个进程的多个RocksDB实例之间共享,这允许用户控制总的缓存大小。

用户可以配置两个Block Cache,默认的是存储未压缩块的Block Cache,还可以单独配置一个存储压缩块的Block Cache。读取的时候会先拉去未压缩的数据块的缓存,然后才拉取压缩数据块的缓存。在打开直接IO的时候压缩块缓存可以替代OS的页缓存。配置项如下:

# 设置存储未压缩的块
table_options.block_cache = cache;
# 设置存储压缩的块
table_options.block_cache_compressed = another_cache;

RocksDB里面有两种实现方式,分别叫做LRUCache和ClockCache。两个类型的缓存都通过分片来减轻锁冲突。容量会被平均的分配到每个分片,分片之间不共享空间。默认情况下,每个缓存会被分片到64个分片,每个分片至少有512kB空间。

开箱即用的情况下,RocksDB会使用LRU块缓存实现,空间为8MB。如果希望使用自定义的块缓存,调用额外LRUCache()或者NewClockCache()来创建一个缓存对象,然后把它设置到基于块的表选项。用户也可以使用自己实现的缓存,只需要实现Cache接口即可

WAL

RocksDB中的每个更新操作都会写到两个地方:

  • 一个内存数据结构,名为memtable(后面会被刷盘到SST文件)

  • 写到磁盘上的WAL日志。

在出现服务崩溃的时候,WAL日志可以用于完整的恢复memtable中的数据,以保证数据库能恢复到原来的状态并且数据不丢失。

SST

SST文件是RocksDB在磁盘上的file结构,sstfile由block作为基本单位组成,一个sstfile结构由多个data block和meta block组成,其中data block就是数据实体block,meta block为元数据block。sstfile组成的block有可能被压缩(compression),不同level也可能使用不同的compression方式。sstfile如果要遍历block,会逆序遍历,从footer开始。

MANIFEST

RocksDB对文件系统以及存储介质保持不可预知的态度。文件系统操作不是原子的,并且在系统错误的时候容易出现不一致。即使打开了日志系统,文件系统还是不能在一个不合法的重启中保持一致。POSIX文件系统不支持原子化的批量操作。因此,无法依赖RocksDB的数据存储文件中的元数据文件来构建RocksDB重启前的最后的状态。

RocksDB有一个内建的机制来处理这些POSIX文件系统的限制,这个机制就是保存一个名为MANIFEST的ROCKSDB状态变化的事务日志文件。MANIFEST文件用于在重启的时候,恢复rocksdb到最后一个一致的一致性状态。

下面有三个术语需要区分一下:

  • MANIFEST:指通过一个事务日志,来追踪Rocksdb状态迁移的系统

  • Manifest日志:指一个独立的日志文件,它包含RocksDB的状态快照/版本

  • CURRENT:指最后的Manifest日志

Iterator && Snapshot

Iterator方法提供用户RangeScan功能,首先seek到一个特定的key,然后从这个点开始遍历。Iterator也可以实现RangeScan的逆序遍历,当执行Iterator时,用户看到的是一个时间点的一致性视图。

大部分的LSM引擎都不支持高效的RangeScan操作,这是由于执行RangeScan操作时都要访问所有的数据文件导致。但是大部分用户并不仅仅是完全scan所有的数据,相反,很多情况下仅仅需要按照key的前缀字符串去遍历。RocksDB根据这些应用场景,优化了对应的底层实现。用户可以prefix_extractor来声明一个key_prefix,然后RocksDB为每一个key_prefix存储相应的blooms。配置了key_prefix的Iterator操作可以通过对应的bloom bits来避免检索不含有特定key prefix的数据文件,以此可以提高Iterator性能。

Snapshot接口可以创建数据库在某一个时间点的快照。Iterator接口也可以执行在某一个Snapshot上。

某种意义上,Iterator和Snapshot提供了DB在某个时间点的一个一致性视图,但是其实现原理却不一样。快速短期/前台的scan操作比较适合用Iterator,长期/后台操作适合用Snapshot。当使用Iterator时,会对数据库相应时间点的所有底层文件增加引用计数,直到Iterator结束或者释放了引用计数后,这些文件才允许被删除。

Snapshot不关注数据文件是否被删除的问题,Compation进程会感知Snapshot的存在,会保证对应视图的数据不会被删除。当实例重启时,Snapshot会丢失,这是因为RocksDB不会持久化Snapshot相关数据。

操作概念

Flush

在RocksDB中,最终数据的持久化都是保存在SST中,而SST则是由Memtable刷新到磁盘生成的,这就是所谓的Flush过程。此处仅讲述概念,下一篇基础操作中详细讲解Flush过程

Compaction

英文翻译是压缩(与Compression翻译一致,容易混淆),但是我更喜欢称之为合并,因为这个动作做的事情就是SST的合并并下降一层迁移。此处仅讲述概念,下一篇基础操作中详细讲解Compaction过程。

Compression

在每个SST文件里,数据块和索引块会被分别压缩。用户可以指定压缩类型。压缩配置是针对每个列族的。此处仅讲述概念,下一篇基础操作中详细讲解Compression过程。

Ingest

很多时候,我们使用数据库时会有离线向数据库导入数据的需求。比如大量用户在本地的一些离线数据,想要将这一些数据导入到已有的数据库中;或者说NewSQL场景中部分机器离线,重新上线之后的数据增量/全量同步 等场景。这个时候 我们并不想要让这一些数据占用过多的系统资源,更不希望他们对正常的线上业务有影响,所以高效的离线数据导入功能基本上都是数据库必备的功能,RocksDB也不例外。在RocksDB中离线导入的功能是通过Ingest的实现。此处仅讲述概念,下一篇基础操作中详细讲解Ingest过程。

总结

上面的内容比较多,基本上涵盖了RocksDB常用的绝大部分概念,可能需要好好的理解下,这对后续学习RocksDB非常关键,但是相信熟悉HBase以及LSM tree的小伙伴对这些概念应该绝大部分都熟悉,应该比较好上手,如果不是很熟悉,可以看看之前的两篇文章回顾下。

  • 俗话说别在一棵树上吊死,那为什么那么多NOSQL都喜欢在LSM树上吊死呢?

  • HBase生产环境从入门到熟练使用,这一篇文章就够了

后续会在这篇文章的基础上展开讲一下RocksDB相关的属性以及操作。

最后,笔者长期关注大数据通用技术,通用原理以及NOSQL数据库的技术架构以及使用。如果大家感觉笔者写的还不错,麻烦大家多多点赞和分享转发,也许你的朋友也喜欢。

最后挂个公众号二维码,欢迎大家关注,谢谢大家支持。

漫谈RocksDB(二)基础讲解——仿佛兮若轻云之蔽月,飘飘兮若流风之回雪相关推荐

  1. 仿佛兮若轻云之蔽月,飘飘兮若流风之回雪

    仿佛兮若轻云之蔽月,飘飘兮若流风之回雪.一种名状,是一种墨迹,一种发自潜意识的. 请问:大家懂我说的这句话的意思吗?程序员的生活要种诗意,偶尔写写小散文,为什么程序员不能成为诗人呢? 只需要一种名状和 ...

  2. 架构漫谈(二):认识概念是理解架构的基础

    原文:架构漫谈(二):认识概念是理解架构的基础 架 构漫谈是由资深架构师王概凯Kevin执笔的系列专栏,专栏将会以Kevin的架构经验为基础,逐步讨论什么是架构.怎样做好架构.软件架构如何落地.如 何 ...

  3. 如何在linux里面运行ncl,NCL基础讲解(二)——NCL安装与运行

    NCL基础讲解(二)--NCL安装与运行 兰溪之水2015-11-04 上一期已经给大家简单介绍了NCL的基本情况,既然NCL在科学数据分析和可视化方面有那么多优点,那还等什么,让我们一起" ...

  4. C/C++基础讲解(二十六)之数值计算与趣味数学篇(打鱼还是晒网与怎样存钱以获取最大利息)

    C/C++基础讲解(二十六)之数值计算与趣味数学篇(打鱼还是晒网与怎样存钱以获取最大利息) 程序之美 前言 很多时候,特别是刚步入大学的学子们,对于刚刚开展的计算机课程基本上是一团迷雾,想要弄明白其中 ...

  5. C/C++基础讲解(二十九)之数值计算与趣味数学篇(百钱百鸡问题、爱因斯坦的数学题、三色球问题与马克思手稿中的数学题)

    C/C++基础讲解(二十九)之数值计算与趣味数学篇(百钱百鸡问题.爱因斯坦的数学题.三色球问题与马克思手稿中的数学题) 程序之美 前言 很多时候,特别是刚步入大学的学子们,对于刚刚开展的计算机课程基本 ...

  6. 漫谈RocksDB(一)简介——家有美女初长成,一朝成名天下知

    漫谈RocksDB(一)简介--家有美女初长成,一朝成名天下知 - 墨天轮前言 经过一段时间的锻炼和适应,笔者已经慢慢适应了公众号发文的节奏,经过前段时间的知识整理以及阅读反馈,笔者感觉在未来一段时间 ...

  7. mysql 导出dmp文件_MySQL数据库基础讲解

    # 简介 MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),使用最常用的数据库管理语言(SQL)进行数据库管理. MySQL是开放源代码的,因此任何人都可以在General Publi ...

  8. 机器学习算法平台alink_Alink漫谈(十二) :在线学习算法FTRL 之 整体设计

    Alink漫谈(十二) :在线学习算法FTRL 之 整体设计 [Toc] 0x00 摘要 Alink 是阿里巴巴基于实时计算引擎 Flink 研发的新一代机器学习算法平台,是业界首个同时支持批式算法. ...

  9. Alink漫谈(八) : 二分类评估 AUC、K-S、PRC、Precision、Recall、LiftChart 如何实现

    Alink漫谈(八) : 二分类评估 AUC.K-S.PRC.Precision.Recall.LiftChart 如何实现 文章目录 Alink漫谈(八) : 二分类评估 AUC.K-S.PRC.P ...

最新文章

  1. Hadoop1 Centos伪分布式部署
  2. 用区块链变革教育行业?全球首个教育+旅行+区块链平台——Ambertime:让每个人都能够将时间凝结成自己专属的“琥珀”...
  3. 解决锚点在IE8中失效
  4. PostgreSQL 配置内存参数
  5. 如何成为一名优秀的关卡设计师?
  6. 渗透测试工具包 | 开源安全测试工具 | 网络安全工具
  7. camera理论基础和工作原理
  8. 数格子算面积的方法_数方格在平面图形面积公式推导教学中的妙用
  9. 医疗器械安规三项是什么?1、漏电流测试 IEC60950-1 2、电介质强度测试=耐压测试?GB9706 3、保护接地电阻测试=保护接地 ?GB9706
  10. Windows Audio无法启动 错误 0x80070005:拒绝访问
  11. 为啥不招北大清华的?
  12. 使用Python+Pandas+Statsmodels建立线性回归模型预测房价
  13. chatgpt国内能用吗?详细解读gpt的使用方法
  14. Python光的干涉仿真
  15. Spin和Promela复习
  16. 函数图像变换的规律,以一元函数和二元函数为例来说明,对多元函数同样适用。
  17. AE学习笔记 环绕粒子光线特效
  18. thinkcmf调用子类
  19. 微信小程序开发学习文档(万字总结,一篇搞定前端开发)
  20. media属性和媒体类型

热门文章

  1. 吵架必备的99句英语口语
  2. 宝宝喝奶粉过敏怎么办?
  3. BZOJ 4668 冷战——并查集+LCA
  4. 11.Flink ProcessFunction介绍及KeyedProcessFunction实例
  5. java 销毁cmd窗口
  6. 微信防封养号规则大全(最新整理)
  7. foss测试_FOSS会议的初学者指南
  8. forEach用法与map用法区别
  9. [BZOJ1430] 小猴打架
  10. RuntimeError: 1only batches of spatial targets supported (3D tensors) but got targets of size: : [1]