MANIFEST
概览
RocksDB与文件系统和存储介质无关。文件系统操作不是原子性的,并且在发生系统故障时容易出现不一致。 即使打开日志记录,文件系统也不能保证不干净重启时的一致性。POSIX文件系统也不支持操作的原子批处理。 因此,不可能依赖嵌入到RocksDB数据存储文件中的元数据来重建重启时RocksDB的最后一个一致状态。
RocksDB有一个内置的机制,通过保存一个名为MANIFEST的RocksDB状态更改的事务日志来克服POSIX文件系统的这些限制。 MANIFEST用于在重启时将RocksDB恢复到最新的一致状态。
术语
- MANIFEST指的是在事务日志中跟踪RocksDB状态变化的系统
- Manifest log 指包含RocksDB状态snapshot/edits的单个日志文件
- CURRENT 指的是最新的清单日志
它是如何工作的?
MANIFEST是RocksDB状态更改的事务日志。MANIFEST是由 - manifest log 文件和最新的manifest文件指针组成。 Manifest logs是名为MANIFEST-(seq编号)的滚动日志文件。序号总是在增加。CURRENT是一个特殊的文件,它标识最新的manifest log文件。
在系统(re)start时,最新的manifest log包含RocksDB的一致状态。对RocksDB状态的任何后续更改都将记录到manifest log文件中。 当manifest log文件超过一定大小时,将使用RocksDB状态快照创建一个新的manifest log文件。 更新最新的manifest log指针,并同步文件系统。成功更新到当前文件后,将清除冗余manifest log。
MANIFEST = { CURRENT, MANIFEST-<seq-no>* }
CURRENT = File pointer to the latest manifest log
MANIFEST-<seq no> = Contains snapshot of RocksDB state and subsequent modifications
版本编辑
在任何给定时间,RocksDB的特定状态都称为版本(即快照)。对版本的任何修改都被认为是版本编辑。 版本(或RocksDB状态快照)是通过连接一系列版本编辑来构建的。本质上,manifest log文件是一系列版本编辑。
version-edit = Any RocksDB state change
version = { version-edit* }
manifest-log-file = { version, version-edit* }
= { version-edit* }
版本编辑布局
Manifest log是一系列版本编辑记录。版本编辑记录类型由编辑标识号标识。 我们使用以下数据类型进行encoding/decoding
数据类型
简单数据类型
VarX - Variable character encoding of intX
FixedX - Fixed character encoding of intX
复杂数据类型
String - Length prefixed string data
+-----------+--------------------+
| size (n) | content of string |
+-----------+--------------------+
|<- Var32 ->|<-- n -->|
版本编辑记录格式
版本编辑记录具有以下格式。解码器使用记录标识号标识记录类型
+-------------+------ ......... ----------+
| Record ID | Variable size record data |
+-------------+------ .......... ---------+
<-- Var32 --->|<-- varies by type -->
版本编辑记录类型和布局
针对RocksDB的不同状态变化,有多种编辑记录。 比较器编辑记录:
Captures the comparator name
+-------------+----------------+
| kComparator | data |
+-------------+----------------+
<-- Var32 --->|<-- String -->|
日志编号编辑记录:
Latest WAL log file number
+-------------+----------------+
| kLogNumber | log number |
+-------------+----------------+
<-- Var32 --->|<-- Var64 -->|
以前的文件号编辑记录:
Previous manifest file number
+------------------+----------------+
| kPrevFileNumber | log number |
+------------------+----------------+
<-- Var32 --->|<-- Var64 -->|
下一个文件编号编辑记录:
Next manifest file number
+------------------+----------------+
| kNextFileNumber | log number |
+------------------+----------------+
<-- Var32 --->|<-- Var64 -->|
最后序列号编辑记录:
Last sequence number of RocksDB
+------------------+----------------+
| kLastSequence | log number |
+------------------+----------------+
<-- Var32 --->|<-- Var64 -->|
最大列族编辑记录:
Adjust the maximum number of family columns allowed.
+---------------------+----------------+
| kMaxColumnFamily | log number |
+---------------------+----------------+
<-- Var32 --->|<-- Var32 -->|
删除文件编辑记录:
Mark a file as deleted from database.
+-----------------+-------------+--------------+
| kDeletedFile | level | file number |
+-----------------+-------------+--------------+
<-- Var32 --->|<-- Var32 -->|<-- Var64 -->|
新增文件编辑记录:
将文件标记为新添加到数据库并提供RocksDB元信息。
文件编辑记录与压缩信息
+———————+——————-+———————+——————+————————+———————+————————+————————+ | kNewFile4 | level | file number | file size | smallest_key | largest_key | smallest_seqno | largest_seq_no | +———————+——————-+———————+——————+————————+———————+————————+————————+ |<— var32 —>|<— var32 —>|<— var64 —>|<- var64 ->|<— String —>|<— String —>|<— var64 —>|<— var64 —>|
+———————+—————————+————-+———+————————+——————————+————-+——————+ | CustomTag1 | Field 1 size n1 | field1 | … | CustomTag(m) | Field m size n(m) | field(m)| kTerminate | +———————+—————————+————-+———+————————+——————————+————-+——————+ <— var32 —>|<— var32 —>|<- n1 ->| |<— var32 - ->|<— var32 —>|<- n(m)->|<- var32 —>|
可以在其中编写几个可选的自定义字段。字段有一个特殊的位,指示是否可以安全地忽略它。这是为了兼容性。RocksDB较老版本可能会看到它无法识别的字段。 通过检查位,RocksDB知道是应该停止打开数据库,还是忽略字段。
支持几个可选的自定义字段:kneedcompression:是否应该将文件压缩到下一层。kMinLogNumberToKeepHack:这个条目之后仍然需要恢复的WAL文件号。 kPathId:文件所在的路径ID。旧版本不能忽略这一点。
文件编辑记录向后兼容
+———————+——————-+———————+——————+————————+———————+————————+————————+ | kNewFile2 | level | file number | file size | smallest_key | largest_key | smallest_seqno | largest_seq_no | +———————+——————-+———————+——————+————————+———————+————————+————————+ <— var32 —>|<— var32 —>|<— var64 —>|<- var64 ->|<— String —>|<— String —>|<— var64 —>|<— var64 —>|
文件编辑记录与路径信息
+———————+——————-+———————+——————-+——————-+————————+———————+ | kNewFile3 | level | file number | Path ID | file size | smallest_key | largest_key | +———————+——————-+———————+——————-+——————-+————————+———————+ |<— var32 —>|<— var32 —>|<— var64 —>|<— var32 —>|<— var64 —>|<— String —>|<— String —>| +————————+————————+ | smallest_seqno | largest_seq_no | +————————+————————+ <— var64 —>|<— var64 —>|
列族状态编辑记录:
Note the status of column family feature (enabled/disabled)
+------------------+----------------+
| kColumnFamily | 0/1 |
+------------------+----------------+
<-- Var32 --->|<-- Var32 -->|
列族添加编辑记录:
Add a column family
+---------------------+----------------+
| kColumnFamilyAdd | cf name |
+---------------------+----------------+
<-- Var32 --->|<-- String -->|
列族删除编辑记录:
Drop all column family
+---------------------+
| kColumnFamilyDrop |
+---------------------+
<-- Var32 --->|
记录为原子组的一部分(因为RocksDB 5.16):
在某些情况下,”全有或全无”、多列家族版本更改是可取的。例如,原子刷新确保所有或没有一个列族被成功地刷新,多个列族外部SST摄取保证所有或没有一个列族成功地摄取SST。 由于编写多个版本的编辑不是原子性的,我们需要采取额外的措施来实现原子性(从用户的角度来看,不一定是即时的)。 因此,我们引入了一个新的记录字段kInAtomicGroup,以表明该记录是遵循“全有或全无”属性的一组版本编辑的一部分。格式如下。
+-----------------+--------------------------------------------+
| kInAtomicGroup | #remaining version edits in the same group |
+-----------------+--------------------------------------------+
|<--- Var32 ----->|<----------------- Var32 ------------------>|
在恢复期间,RocksDB缓冲原子组的版本编辑,直到从清单文件中成功解码原子组的最后版本编辑,才应用它们。然后,RocksDB应用这个原子组中的所有版本编辑。RocksDB从不应用部分原子组。
版本编辑可忽略的记录类型
我们在记录类型中保留了一个特殊的位。如果位设置好了,就可以安全地忽略它。可安全忽略的记录有标准的一般格式:
+---------+----------------+----------------+
| kTag | field length n | fields ... |
+--------------------------+----------------+
<- Var32->|<-- var32 -->|<--- n >|
这是在6.0中引入的,还没有创建定制的ignoreable记录。