LevelDB 01: 数据安全
目录:
1. 原子性 Atomicity
ReadPhysicalRecord():
- header eof, 但读到的<kHeaderSize, 则知道header不完整.
- header完整, 但对应的payload数据不够.
- 检查 checksum
CURRENT文件的原子: 通过mv来实现, 保证CURRENT的内容是有效的. see SetCurrentFile()
leveldb::WriteBatch batch;
batch.Delete("key");
batch.Put("key2", value);
db->Write(leveldb::WriteOptions(), &batch);
WriteBatch中 rep_
中虽然有存储batch需要完成的个数.
WriteBatch::Put -> WriteBatchInternal::SetCount 修改 ->rep_[8]
Writer::AddRecord -> Writer::EmitPhysicalRecord
但这个并不作为完整性校验的依据.
原子的实现 和 非batch 的方法一样.
2. Consistency
2.1 有没有事务, log没有保存旧值, 如何撤销?
问题: log record内容是 事务id, 元素, 旧值, 新值, 对吗?
答:不对. 因为不支持事务, 也不存储旧值.
问题: 检查到 corrupt 的记录后, 如何处理, 有没有将这个corrupt的记录抹掉,代码在?
答:
VersionSet::Recover
{
while (reader.ReadRecord(&record, &scratch) && s.ok()) {
s = edit.DecodeFrom(record);
}
}
没有抹掉 corrupt 的数据.
问题: open, 不写入key, 然后关闭, 后续, 是否会重复同一个文件的 Recover ?
DB::Open
{
VersionEdit edit;
bool save_manifest = false;
impl->Recover(&edit, &save_manifest);
if (save_manifest) {
edit.SetPrevLogNumber(0);// No older logs needed after recovery.
edit.SetLogNumber(impl->logfile_number_);
}
}
问题: 判断是否有log 需要恢复?
答: 参与恢复的log, 其编号 需要大于LogNumber().
DBImpl::Recover
|--VersionSet::Recover(bool *save_manifest)
| |--log::Reader reader(file,.., 0/*initial_offset*/);
| |--reader.ReadRecord(&record, &scratch)
| |--log_number_, has_prev_log_number_
|--Recover from all newer log files than the ones named in the descriptor
|--const uint64_t min_log = versions_->LogNumber();
|--env_->GetChildren(dbname_, &filenames) //读取目录
|--for each of filenames
| |--if (type == kLogFile && ((number >= min_log) {logs.push_back(number)}
|--for each of logs
| |--RecoverLogFile
2.2 (TODO)基于leveldb, filestore 是否实现了 事务/commit等?
3. log
uint64_t new_log_number = versions_->NewFileNumber();
所以, log编号的作用域是VersionSet.
3.1 写log在哪里
当应用写入一条Key:Value记录的时候,LevelDb会先往log文件里写入,成功后将记录插进Memtable中,这样基本就算完成了写入操作. 代码?
答:
MakeRoomForWrite
{
// Attempt to switch to a new memtable and trigger compaction of old
// 1.memtable满了 或者 2.没满但force
//因为background_work_finished_signal_.Wait完成, 压缩完成, 也就是没有log正在被压缩, 故PrevLogNumber()为0.
assert(versions_->PrevLogNumber() == 0);
WritableFile* lfile = nullptr;
env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile);
logfile_ = lfile;
log_ = new log::Writer(lfile);
}
DBImpl::Write
|--log_->AddRecord
| |--dest_->Append()
|--if (options.sync) logfile_->Sync()
|--以下是写内存
|--WriteBatchInternal::InsertInto(updates, mem_)
| |--MemTableInserter inserter;
| |--inserter.mem_ = memtable;
| |--updates->Iterate(&inserter)
| | |--Slice input(rep_);
| | |--case kTypeValue:
| | | |--handler->Put(key, value) => MemTableInserter::Put
| | | |--mem_->Add(sequence_, kTypeValue, key, value)
| | | | |--SkipList
4. RecoverLogFile
RecoverLogFile
|--while
| |--reader.ReadRecord(&record, &scratch)
| |--WriteBatchInternal::SetContents(&batch, record)
| | |--把record 放入 b->rep_
| |--if (mem == nullptr) mem = new MemTable;
| |--WriteBatchInternal::InsertInto(&batch, mem)
| | |-- input(也就是record) 格式是: (WriteBatch::rep_的格式)
| | |-- seq(8B) cnt(4B)tag key(+value) tag key(+value)
| | |-- |---------|------|--|------------|--|-------------|
| | |--while
| | | |--MemTableInserter::Put 或Delete, 放入 MemTable
| | | |--sequence_++
| |--if (mem > write_buffer_size) WriteLevel0Table
5. VersionSet::Recover
VersionSet::Recover
|--Builder builder(this, current_);
|--while
| |--reader.ReadRecord(&record, &scratch)
| |--VersionEdit edit;
| |--edit.DecodeFrom(record);
| | |--case kCompactPointer:
| | | |--GetLevel
| | | |--GetInternalKey
| | | |--compact_pointers_.push_back(std::make_pair(level, key))
| | |--case kDeletedFile:
| | | |--deleted_files_.insert(std::make_pair(level, number))
| | |--case kNewFile:
| | | |--FileMetaData f
| | | |--new_files_.push_back(std::make_pair(level, f))
| |--builder.Apply(&edit); 将edit 应用到 Builder, 之后, edit就可以丢弃
|--Version* v = new Version(this); // 基于VersionSet创建Version
|--builder.SaveTo(v) // Version* v
| |--将base_ 和 levels_中added_files, 按递增顺序 放入 v
| | |--MaybeAddFile -> push to v->files_[level]
|--Finalize(v) 计算下次从哪个level进行压缩.
|--AppendVersion(v); current_ 指向@v
要指明一个文件, 需要给出level 和 number. 比如 Level 4: File4_0, File4_1, File4_2, File4_3
compact_pointers_ 见 https://awakening-fong.github.io/posts/database/leveldb_04_compact
VersionSet是版本的集合, 从 AppendVersion() 就有体现. VersionEdit 不是 VersionSet. VersionEdit 的意思是版本经过 edit后, 变成另一个版本, 所以, VersionEdit可以认为是差异.
本文地址: https://awakening-fong.github.io/posts/database/leveldb_01_data_safe
转载请注明出处: https://awakening-fong.github.io
若无法评论, 请打开JavaScript, 并通过proxy.
blog comments powered by Disqus