漫谈RocksDB(四)存储结构——翩若惊鸿,婉若游龙

2023-10-29 22:20

本文主要是介绍漫谈RocksDB(四)存储结构——翩若惊鸿,婉若游龙,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

前面几个章节已经分别说了下RocksDB的基础概念和相关操作,下面来说一说RocksDB的存储结构,根据文件的存储结构再去回过头了解下RocksDB的相关操作。

正文

文件介绍

  • *.log: 事务日志用于保存数据操作日志,可用于数据恢复

  • *.sst: 数据持久换文件(此处的例子没有生成sst文件是因为第一次写数据,数据量小没触发flush操作,数据都在内存的MemoryTable中)

  • MANIFEST:数据库中的 MANIFEST 文件记录数据库状态。Compaction过程会添加新文件并从数据库中删除旧文件,并通过将它们记录在 MANIFEST 文件中使这些操作持久化。

  • CURRENT:记录当前正在使用的MANIFEST文件

  • LOCK:rocksdb自带的文件锁,防止两个进程来打开数据库。

  • IDENTITY:id

  • LOG:统计日志

  • OPTIONS:配置信息

写操作先写WAL,再写memtable,memtable达到一定阈值后切换为Immutable Memtable,只能读不能写。后台Flush线程负责按照时间顺序将Immutable Memtable刷盘,生成level 0 层的有序文件(SST)。后台合并线程负责将上层的SST合并生成下层的SST。

Manifest负责记录系统某个时刻SST文件的视图,Current文件记录当前最新的Manifest文件名。 每个ColumnFamily有自己的Memtable, SST文件,所有ColumnFamily共享WAL、Current、Manifest文件,用户可以基于RocksDB构建自己的column families。很多应用程序把RocksDB当做库(libary),尽管他提供server或者CLI接口。

上面是文件的具体含义以及处理流程,下面讲一下MemoryTable以及SST两个主要的数据存储结构,其余的存储结构了解含义与作用即可,日常工作中基本上不太会需要了解实现细节。如果确实有需要的,大家可以自行学习下。

MemoryTable

RocksDB 的 memtable 的默认实现是一个 skiplist。skiplist 是一个有序的数据集,当业务场景是使用 range-scans 并且同时写入时,这是一个很有效的数据结构。

然而,一些应用程序不同时写入和扫描,而另一些应用程序根本不执行范围扫描。对于这些应用程序,skiplist可能无法提供最佳性能。因此,RocksDB 支持可插拔的 API,允许应用程序提供自己的 memtable 实现。

开发库提供了三个 memtable:

  • skiplist memtable,

  • vector memtable

  • 前缀散列(prefix-hash) memtable。

Vector memtable 适用于将数据批量加载到数据库中。每个写入在向量的末尾插入一个新元素; 当它是刷新 memtable 到存储的时候,向量中的元素被排序并写出到 L0 中的文件。

前缀散列 memtable 允许对 gets,puts 和 scans-within-a-key-prefix 进行有效的处理。

如果非上面的两种情况,建议使用默认的skiplist即可。

Skiplist Memtable

基于Skiplist的memtable在多数情况下都有较好读,写,随机访问以及序列化扫描性能。除此之外,他还提供其他memtable没有的有用的功能,比如并发插入以及带Hint插入。

Hash xxxx Memtable

hash类型的Memtable有两种不同的实现,一种是HashSkipList,另一种是HashLinkList。正如他们的名字所描述的,HashSkipList用一张哈希表组织数据,每个哈希桶内都是一个的skiplist,而HashLinkList则是用一张哈希表组织数据,每个哈希桶内则是使用一个排序好的linkedlist。两种类型都是为了减少查询的时候的比较次数,而两者之间的差别其实就是skiplist与linkedlist的差别。一种好的使用例子是使用PlainTable SST格式结合他们,然后把数据存储在RAM FS里。

当做数据查询或者插入一个key的时候,目标key的前缀通过Options.prefix_extractor被提取出来,用于找到具体的哈希桶。在哈希桶里面,所有的比较都是完整(内部)key比较,跟SkipList的memtable一样。

使用基于哈希的memtable最大的限制就是做多个前缀扫描的时候需要拷贝和排序,这非常慢并且浪费内存。

SSTable

SSTable 是 Sorted String Table 的简称,是大名鼎鼎的 Bigtable 底层的数据存储格式。HBase以及Cassandra的底层硬盘存储也是基于此格式。

SSTable简介

SSTable 的格式为文件本身就是一个排序的、不可变的、持久的Key/Value对Map。其中Key和value都可以是任意的byte字符串。 KeyValue 对根据固定比较规则有序地写入到文件中,文件内部分成一系列的Blocks(Block 不会太大,可自定义,默认4KB,常见的是 64KB,RocksDB对应配置项:table_options.block_size),同时具有必要的索引信息。这样 就既可以顺序地读取内部 KeyValue 记录,也能够根据某个 Key 值进行快速定位。

如图所示,SST 文件从头到尾分成5个部分。

名称占用空间说明
Footer固定48字节指出 IndexBlock 和 MetaIndexBlock 在文件中的偏移量信息,它是元信息的元信息,它位于 sstable 文件的尾部
IndexBlock占用一个 block 空间记录了 DataBlock 相关的元信息
MetaIndexBlock占用一个 block 空间各个元信息的Block,包括Filter、Properties(整个table的属性信息)、Compression dictionary、Range deletion tombstone
MetaBlock可能占用多个 block空间存储布隆过滤器的二进制数据 及其他元信息数据
DataBlock可能占用多个 block空间存储实际的数据即键值对内容

Block

从上面的表格可以看出RocksDB的SSTable除了Footer外其余都是使用Block进行存储的,Block在硬盘上存储的时候,会在实际数据之后加上5个字节的额外内容:compression type、crc,如下图所示:

Bloom Filter

这里再说一下MetaBlock中的Bloom Filter,Bloom Filter的作用是对于任意集合的key,基于BitMap可以用来构建一个bit数组的算法。给出任意key,这个bit数组可以用于决定这个key是不是可能存在或者绝对不存在于这个key集合,用来减少无用的查询次数,从而加快查询的速度。Bloom Filter具体的知识点可以查看我之前的文章-------------

Footer

以上各部分都是 Block 的结构,只有 Footer 不同,是一个定长的格式。

序列化后,Footer的长度固定,为48个字节(旧)或53字节(新),格式如下:

// legacy footer format:
//    metaindex handle (varint64 offset, varint64 size)
//    index handle     (varint64 offset, varint64 size)
//    <padding> to make the total size 2 * BlockHandle::kMaxEncodedLength
//    table_magic_number (8 bytes)
// new footer format:
//    checksum type (char, 1 byte)
//    metaindex handle (varint64 offset, varint64 size)
//    index handle     (varint64 offset, varint64 size)
//    <padding> to make the total size 2 * BlockHandle::kMaxEncodedLength + 1
//    footer version (4 bytes)
//    table_magic_number (8 bytes)

Footer 中的信息,指明了 MetaIndexBlock 和 IndexBlock的位置,进而找到 MetaBlock 和 DataBlock。

读取 SST文件的时候,就是从文件末尾,固定读取这48或53字节,进而得到了 Footer 信息。

总结

上文讲解的就是RocksDB相关的存储知识,基本上能覆盖到常用的部分,其余需要详细理解的小伙伴们可以按需学习,另外不是管MemoryTable还是SSTable都是LSM tree的基础组件,被广泛利用于各种LSM tree的实现中,所以还是值得学习下的。

最后路漫漫其修远兮,大数据之路还很漫长。如果想一起大数据的小伙伴,欢迎点赞转发加关注,下次学习不迷路,我们在大数据的路上共同前进!

最后挂个公众号二维码,公众号的文章是最新的,CSDN的会有些滞后,想追更的朋友欢迎大家关注公众号,谢谢大家支持。 

公众号地址:

 

这篇关于漫谈RocksDB(四)存储结构——翩若惊鸿,婉若游龙的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/303731

相关文章

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

使用JavaScript操作本地存储

《使用JavaScript操作本地存储》这篇文章主要为大家详细介绍了JavaScript中操作本地存储的相关知识,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... 目录本地存储:localStorage 和 sessionStorage基本使用方法1. localStorage

结构体和联合体的区别及说明

《结构体和联合体的区别及说明》文章主要介绍了C语言中的结构体和联合体,结构体是一种自定义的复合数据类型,可以包含多个成员,每个成员可以是不同的数据类型,联合体是一种特殊的数据结构,可以在内存中共享同一... 目录结构体和联合体的区别1. 结构体(Struct)2. 联合体(Union)3. 联合体与结构体的

PostgreSQL如何查询表结构和索引信息

《PostgreSQL如何查询表结构和索引信息》文章介绍了在PostgreSQL中查询表结构和索引信息的几种方法,包括使用`d`元命令、系统数据字典查询以及使用可视化工具DBeaver... 目录前言使用\d元命令查看表字段信息和索引信息通过系统数据字典查询表结构通过系统数据字典查询索引信息查询所有的表名可

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s