概述

每次对RocksDB的更新都被写到两个地方: 1) 内存中称为memtable的数据结构(稍后刷新到SST文件) 2)在磁盘上提前写入日志(WAL)。如果发生故障,可以使用写前日志完全恢复memtable中的数据,这对于将数据库恢复到原始状态是必要的。 在默认配置中,RocksDB通过在每个用户写完之后对WAL进行fflush()调用来保证进程崩溃的一致性。

WAL的生命周期

让我们用一个例子来说明WAL的生命周期。RocksDB instace db由两个列家族”new_cf”和”default”创建。 一旦打开数据库,就会在磁盘上创建一个新的WAL来保存所有的写操作。

  1. DB* db;
  2. std::vector<ColumnFamilyDescriptor> column_families;
  3. column_families.push_back(ColumnFamilyDescriptor(
  4. kDefaultColumnFamilyName, ColumnFamilyOptions()));
  5. column_families.push_back(ColumnFamilyDescriptor(
  6. "new_cf", ColumnFamilyOptions()));
  7. std::vector<ColumnFamilyHandle*> handles;
  8. s = DB::Open(DBOptions(), kDBPath, column_families, &handles, &db);

两个列族都添加了一些键值对

  1. db->Put(WriteOptions(), handles[1], Slice("key1"), Slice("value1"));
  2. db->Put(WriteOptions(), handles[0], Slice("key2"), Slice("value2"));
  3. db->Put(WriteOptions(), handles[1], Slice("key3"), Slice("value3"));
  4. db->Put(WriteOptions(), handles[0], Slice("key4"), Slice("value4"));

此时,WAL应该已经记录了所有的写操作。WAL将保持打开状态,并记录将来的写操作,直到它的大小达到DBOptions::max_total_wal_size。

如果用户决定冲列族”new_cf”,发生了几件事情: 1) new_cf (key1和key3)的数据刷新到一个新的SST文件 2) 创建一个新的WAL和未来所有写入所有列族现在去新的WAL 3) 老的WAL不接受新写但删除可能会被推迟

  1. db->Flush(FlushOptions(), handles[1]);
  2. // key5 and key6 will appear in a new WAL
  3. db->Put(WriteOptions(), handles[1], Slice("key5"), Slice("value5"));
  4. db->Put(WriteOptions(), handles[0], Slice("key6"), Slice("value6"));

此时将有两个WALs,旧的WAL包含key1到key4,新的WAL包含key5和key6。因为旧的WAL仍然包含至少一个列族的活动数据(“default”),所以还不能删除它。只有当用户最终决定刷新”default”列族时,旧的WAL才能自动存档并从磁盘中清除。

  1. db->Flush(FlushOptions(), handles[0]);
  2. // The older WAL will be archived and purged separetely

总而言之,当1) 打开一个新DB, 2)刷新一个列族时,将创建一个WAL。当所有列家族的刷新超过了WAL中包含的最大序列号时,或者换句话说,WAL中的所有数据都被持久化到SST文件中,WAL就会被删除(如果启用了archival,则会存档)。 存档的WALs将被移动到一个隔离的位置,并在稍后从磁盘中清除。实际删除可能由于复制目的而延迟,请参见下面的超越日志迭代器部分。

WAL配置

下面的配置可以在options.h中找到

DBOptions::wal_dir DBOptions::wal_dir 设置RocksDB存储写前日志文件的目录,这允许将WALs存储在与实际数据分开的目录中。

DBOptions::WAL_ttl_seconds, DBOptions::WAL_size_limit_MB 这两个字段影响存档的WALs被删除的速度。非零值表示触发存档WAL删除的时间和磁盘空间阈值。详见 option.h(https://github.com/facebook/rocksdb/blob/5.10.fb/include/rocksdb/options.h#L554-L565)。

DBOptions::max_total_wal_size 为了限制WALs的大小,RocksDB使用DBOptions::max_total_wal_size作为列族刷新的触发器。一旦WALs超过这个大小,RocksDB将开始强制刷新列族,以允许删除一些最古老的WALs。 当以非均匀频率更新列族时,此配置非常有用。如果没有大小限制,当不经常更新的列族有一段时间没有刷新时,用户可能需要保留非常旧的WALs。

DBOptions::avoid_flush_during_recovery 这个配置是不言自明的。

DBOptions::manual_wal_flush DBOptions::manual_wal_flush确定每次写操作之后WAL flush是自动的还是纯手工的(用户必须调用FlushWAL来触发WAL flush)。

DBOptions::wal_filter 通过DBOptions::wal_filter,用户可以提供一个filter对象,以便在恢复期间处理WALs时调用。注意:ROCKSDB_LITE模式不支持

WriteOptions::disableWAL 当用户依赖于其他日志记录或不关心数据丢失时,WriteOptions::disableWAL非常有用。

WAL 过滤器

事务日志迭代器

事务日志迭代器提供了在RocksDB实例之间复制数据的方法。一旦由于列族刷新而归档了WAL,就会归档而不是立即删除它。 目标是允许txn日志迭代器继续读取WAL并发送给slave进行重放。

相关页面

WAL Recovery Modes (https://github.com/facebook/rocksdb/wiki/WAL-Recovery-Modes)

Write Ahead Log File Format (https://github.com/facebook/rocksdb/wiki/Write-Ahead-Log-File-Format)

WAL Performance (https://github.com/facebook/rocksdb/wiki/WAL-Performance)