RocksDB WAL日志 #
一、WAL概述 #
1.1 什么是WAL #
WAL(Write-Ahead Log)是RocksDB的预写日志机制,用于保证数据的持久性和崩溃恢复能力。
text
WAL核心原则:
先写日志,再写数据
写入流程:
写入请求 → WAL日志 → MemTable → 返回成功
崩溃恢复:
读取WAL → 重放日志 → 恢复MemTable数据
1.2 WAL的作用 #
| 作用 | 说明 |
|---|---|
| 持久性保证 | 数据写入WAL后即使崩溃也能恢复 |
| 崩溃恢复 | 通过重放WAL恢复未持久化的数据 |
| 原子性支持 | 批量操作的原子性保证 |
| 复制支持 | 作为复制的数据源 |
二、WAL结构 #
2.1 WAL文件结构 #
text
WAL文件命名:<number>.log
示例:
000001.log
000002.log
文件内容:
┌────────────────────────────────────────────────────┐
│ Record 1 │
│ ┌──────────────────────────────────────────────┐ │
│ │ Header | Type | Key | Value | Checksum │ │
│ └──────────────────────────────────────────────┘ │
├────────────────────────────────────────────────────┤
│ Record 2 │
├────────────────────────────────────────────────────┤
│ ... │
└────────────────────────────────────────────────────┘
2.2 Record格式 #
text
Record结构:
┌────────────────────────────────────────────────────┐
│ Header (7 bytes) │
│ ┌──────────────────────────────────────────────┐ │
│ │ CRC (4B) │ Length (2B) │ Type (1B) │ │
│ └──────────────────────────────────────────────┘ │
├────────────────────────────────────────────────────┤
│ Payload │
│ ┌──────────────────────────────────────────────┐ │
│ │ LogRecord (序列化的写操作) │ │
│ └──────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────┘
Type类型:
- kZeroType: 无效记录
- kFullType: 完整记录
- kFirstType: 分片记录第一片
- kMiddleType: 分片记录中间片
- kLastType: 分片记录最后一片
2.3 LogRecord格式 #
text
LogRecord内容:
┌────────────────────────────────────────────────────┐
│ Header │
│ ┌──────────────────────────────────────────────┐ │
│ │ Type (1B) │ Checksum (4B) │ │
│ └──────────────────────────────────────────────┘ │
├────────────────────────────────────────────────────┤
│ Key │
│ ┌──────────────────────────────────────────────┐ │
│ │ Column Family ID | Sequence Number | Key │ │
│ └──────────────────────────────────────────────┘ │
├────────────────────────────────────────────────────┤
│ Value │
│ ┌──────────────────────────────────────────────┐ │
│ │ Value Length | Value │ │
│ └──────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────┘
三、WAL配置 #
3.1 基本配置 #
cpp
#include <rocksdb/options.h>
rocksdb::Options GetWALOptions() {
rocksdb::Options options;
// WAL目录(默认与数据库同目录)
options.wal_dir = "/path/to/wal";
// WAL文件保留时间(秒)
options.WAL_ttl_seconds = 0; // 0表示不限制
// WAL文件大小限制(MB)
options.WAL_size_limit_MB = 0; // 0表示不限制
// WAL恢复模式
options.wal_recovery_mode = rocksdb::WALRecoveryMode::kPointInTimeRecovery;
return options;
}
3.2 WAL恢复模式 #
cpp
enum WALRecoveryMode {
// 不恢复,忽略WAL
kTolerateCorruptedTailRecords,
// 点恢复,遇到错误停止
kPointInTimeRecovery,
// 完全恢复,跳过错误记录
kAbsoluteConsistency,
// 跳过所有错误
kSkipAnyCorruptedRecords,
};
// 推荐配置
options.wal_recovery_mode = rocksdb::WALRecoveryMode::kPointInTimeRecovery;
3.3 写入选项 #
cpp
#include <rocksdb/options.h>
rocksdb::WriteOptions GetWriteOptions() {
rocksdb::WriteOptions options;
// 同步写入WAL
options.sync = true; // 保证持久性
// 禁用WAL
// options.disableWAL = true; // 提高性能,但可能丢数据
return options;
}
四、WAL写入流程 #
4.1 写入过程 #
text
WAL写入流程:
写入请求 (key, value)
│
▼
┌─────────────────┐
│ 序列化Record │
│ (添加序列号) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 计算CRC校验 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 追加到WAL文件 │
│ (顺序写入) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ sync到磁盘 │
│ (可选) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 写入MemTable │
└────────┬────────┘
│
▼
返回成功
4.2 sync选项影响 #
cpp
// sync = true(同步写入)
rocksdb::WriteOptions sync_options;
sync_options.sync = true;
// 每次写入都调用fsync
// 保证数据持久化
// 性能较低
// sync = false(异步写入)
rocksdb::WriteOptions async_options;
async_options.sync = false;
// 依赖操作系统刷盘
// 可能丢失数据
// 性能较高
五、WAL恢复流程 #
5.1 恢复过程 #
text
WAL恢复流程:
数据库启动
│
▼
┌─────────────────┐
│ 读取MANIFEST │
│ 获取当前状态 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 打开WAL文件 │
│ 按序号排序 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 读取WAL记录 │
│ 验证CRC │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 重放写操作 │
│ 恢复MemTable │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 数据库就绪 │
└─────────────────┘
5.2 恢复示例 #
cpp
#include <rocksdb/db.h>
#include <rocksdb/options.h>
#include <iostream>
int main() {
rocksdb::DB* db;
rocksdb::Options options;
// 设置恢复模式
options.wal_recovery_mode =
rocksdb::WALRecoveryMode::kPointInTimeRecovery;
// 打开数据库(自动恢复)
rocksdb::Status status = rocksdb::DB::Open(options, "/tmp/testdb", &db);
if (!status.ok()) {
std::cerr << "Recovery failed: " << status.ToString() << std::endl;
return 1;
}
std::cout << "Database recovered successfully!" << std::endl;
// 验证数据
std::string value;
status = db->Get(rocksdb::ReadOptions(), "test_key", &value);
delete db;
return 0;
}
六、WAL管理 #
6.1 WAL文件生命周期 #
text
WAL文件生命周期:
1. 创建
- 数据库打开时
- 切换WAL时
2. 使用
- 接收写入操作
- 记录所有变更
3. 归档
- 所有数据已Flush到SST
- 不再需要恢复
4. 删除
- 超过保留时间
- 超过大小限制
6.2 WAL清理 #
cpp
#include <rocksdb/db.h>
void ManageWALFiles(rocksdb::DB* db) {
// 获取WAL文件信息
std::vector<std::string> wal_files;
db->GetSortedWalFiles(wal_files);
for (const auto& file : wal_files) {
std::cout << "WAL file: " << file << std::endl;
}
// 手动Flush触发WAL清理
db->Flush(rocksdb::FlushOptions());
}
6.3 WAL目录分离 #
cpp
#include <rocksdb/options.h>
rocksdb::Options GetSeparateWALOptions() {
rocksdb::Options options;
// 将WAL放在单独的磁盘
options.wal_dir = "/fast_ssd/wal";
// 数据文件放在大容量磁盘
// db_dir = "/large_hdd/data";
return options;
}
七、WAL与列族 #
7.1 多列族WAL #
text
多列族WAL机制:
所有列族共享同一个WAL文件
写入请求:
- CF1: Put(key1, value1)
- CF2: Put(key2, value2)
WAL记录:
┌────────────────────────────────────────┐
│ Record 1: CF1, key1, value1 │
├────────────────────────────────────────┤
│ Record 2: CF2, key2, value2 │
└────────────────────────────────────────┘
恢复时:
根据列族ID恢复到对应MemTable
7.2 原子性保证 #
cpp
#include <rocksdb/db.h>
#include <rocksdb/write_batch.h>
void AtomicCrossCFWrite(rocksdb::DB* db,
rocksdb::ColumnFamilyHandle* cf1,
rocksdb::ColumnFamilyHandle* cf2) {
rocksdb::WriteBatch batch;
// 跨列族操作
batch.Put(cf1, "key1", "value1");
batch.Put(cf2, "key2", "value2");
// 原子写入(WAL保证)
db->Write(rocksdb::WriteOptions(), &batch);
}
八、WAL性能优化 #
8.1 禁用WAL #
cpp
#include <rocksdb/options.h>
// 场景1:批量导入数据
rocksdb::WriteOptions import_options;
import_options.disableWAL = true;
import_options.sync = false;
// 批量写入
for (int i = 0; i < 1000000; i++) {
db->Put(import_options, "key" + std::to_string(i), "value");
}
// 导入完成后手动Flush
db->Flush(rocksdb::FlushOptions());
// 场景2:缓存数据
rocksdb::WriteOptions cache_options;
cache_options.disableWAL = true;
// 数据可以丢失,追求最高性能
8.2 批量写入 #
cpp
#include <rocksdb/write_batch.h>
void BatchWrite(rocksdb::DB* db) {
rocksdb::WriteBatch batch;
// 批量添加操作
for (int i = 0; i < 1000; i++) {
batch.Put("key" + std::to_string(i), "value");
}
// 一次WAL写入
db->Write(rocksdb::WriteOptions(), &batch);
}
8.3 组提交 #
text
组提交机制:
多个写入请求合并为一次WAL写入
请求1 ─┐
请求2 ─┼─→ 组提交 → 一次WAL写入
请求3 ─┘
优点:
- 减少WAL写入次数
- 提高写入吞吐
- 减少fsync开销
九、WAL监控 #
9.1 监控指标 #
cpp
#include <rocksdb/db.h>
#include <iostream>
void PrintWALStats(rocksdb::DB* db) {
uint64_t value;
// WAL文件数量
db->GetIntProperty("rocksdb.num-wal-files", &value);
std::cout << "WAL files: " << value << std::endl;
// WAL文件大小
db->GetIntProperty("rocksdb.wal-file-size", &value);
std::cout << "WAL size: " << value / 1024 / 1024 << " MB" << std::endl;
}
9.2 性能统计 #
cpp
#include <rocksdb/statistics.h>
void PrintWALStatistics(rocksdb::DB* db) {
auto stats = db->GetOptions().statistics;
// WAL写入字节数
uint64_t wal_bytes = stats->getTickerCount(rocksdb::WAL_FILE_BYTES);
std::cout << "WAL bytes written: " << wal_bytes << std::endl;
// WAL sync次数
uint64_t wal_syncs = stats->getTickerCount(rocksdb::WAL_FILE_SYNCED);
std::cout << "WAL syncs: " << wal_syncs << std::endl;
}
十、最佳实践 #
10.1 WAL配置建议 #
| 场景 | sync | disableWAL | 说明 |
|---|---|---|---|
| 关键数据 | true | false | 保证持久性 |
| 高吞吐 | false | false | 平衡性能和安全 |
| 批量导入 | false | true | 最高性能 |
| 缓存数据 | false | true | 可接受数据丢失 |
10.2 故障恢复建议 #
- 选择合适的恢复模式:kPointInTimeRecovery推荐
- 定期备份:WAL不是备份替代品
- 监控WAL大小:防止WAL过大
- 测试恢复流程:确保恢复有效
十一、总结 #
11.1 WAL关键参数 #
| 参数 | 说明 |
|---|---|
| wal_dir | WAL目录 |
| WAL_ttl_seconds | WAL保留时间 |
| WAL_size_limit_MB | WAL大小限制 |
| wal_recovery_mode | 恢复模式 |
| sync | 同步写入 |
| disableWAL | 禁用WAL |
11.2 关键要点 #
- 持久性保证:WAL确保数据不丢失
- 崩溃恢复:通过重放WAL恢复数据
- 性能权衡:sync选项影响性能
- 合理配置:根据场景选择配置
- 监控管理:关注WAL大小和性能
下一步,让我们学习Compaction机制!
最后更新:2026-03-27