leveldb:数据库recover机制
DBImpl::Recover
把數據庫恢復到上次退出的狀態,
Recover的基本功能:如果存在表數據,則Load表數據,并對日志進行恢復,否則,根據flag創建新表或者返回錯誤
Recover的基本流程是:首先是處理創建flag,比如存在就返回失敗等等;然后是嘗試從已存在的sstable文件恢復db;最后如果發現有大于manifest文件記錄的log編號的log文件,則需要回放log(回放的是上一次db關閉時還存在于mem和immem的記錄,因為這些記錄并沒有持久化到磁盤sst文件中),更新db數據。回放期間db可能會dump新的level 0文件,因此需要把db元信息的變動記錄到edit中返回
Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) {mutex_.AssertHeld();// 創建DB目錄,不關注錯誤env_->CreateDir(dbname_);// 在DB目錄下打開或創建(如果不存在)LOCK文件并鎖定它,防止其他進程打開此表Status s = env_->LockFile(LockFileName(dbname_), &db_lock_);if (!s.ok()) {return s;}//判斷CURRENT文件是否存在,current不存在說明數據庫不存在if (!env_->FileExists(CurrentFileName(dbname_))) {//若CURRENT文件不存在,如果options選項設置了create_if_missing,則創建新的dbif (options_.create_if_missing) {s = NewDB();if (!s.ok()) {return s;}} else {//否則返回db不存在return Status::InvalidArgument(dbname_, "does not exist (create_if_missing is false)");}} else {if (options_.error_if_exists) {//如果數據庫存在且設置了error_if_exists,則返回error_if_exists錯誤return Status::InvalidArgument(dbname_, "exists (error_if_exists is true)");}}// 如果運行到此,表明數據庫已經存在,需要load,第一步是從MANIFEST文件中恢復VersionSet s = versions_->Recover(save_manifest);if (!s.ok()) {return s;}SequenceNumber max_sequence(0);/*嘗試從所有比manifest文件中記錄的log要新的log文件中恢復(前一個版本可能會添加新的 log文件,卻沒有記錄在manifest中)。這種情況出現在memtable或者immemtable還沒來得 及寫入sst文件db就掛掉了,因此需要從比manifest文件中記錄的log要新的log文件中恢復*/ //prev_log是早前版本level_db使用的機制,現在以及不再使用,這里只是為了兼容const uint64_t min_log = versions_->LogNumber();const uint64_t prev_log = versions_->PrevLogNumber();std::vector<std::string> filenames;// 列出目錄內的所有文件s = env_->GetChildren(dbname_, &filenames);if (!s.ok()) {return s;}std::set<uint64_t> expected;// 這個函數實質是獲取仍然存活(仍然有效)的文件versions_->AddLiveFiles(&expected);uint64_t number;FileType type;std::vector<uint64_t> logs;//這里先找出所有滿足條件的log文件:比manifest文件記錄的log編號更新。for (size_t i = 0; i < filenames.size(); i++) {if (ParseFileName(filenames[i], &number, &type)) {// 從這里刪除的目的是為了最后看看還有哪些文件名是不能夠解析的expected.erase(number);if (type == kLogFile && ((number >= min_log) || (number == prev_log)))logs.push_back(number);}}// 如果這個數組不為空,那么表示有的文件名解析不了,出錯! if (!expected.empty()) {char buf[50];snprintf(buf, sizeof(buf), "%d missing files; e.g.",static_cast<int>(expected.size()));return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin())));}//找到log文件后,首先排序,保證按照生成順序,依次回放log。回放LOG時,記錄被插入到memtable,如果超過write buffer,則還會dump出level 0的sst文件, std::sort(logs.begin(), logs.end());for (size_t i = 0; i < logs.size(); i++) {// 此方法會將日志種每條記錄的sequence num與max_sequence進行比較,以記錄下最大的sequence num。 并把DB元信息的變動(sstable文件的變動)追加到edit中返回。 s = RecoverLogFile(logs[i], (i == logs.size() - 1), save_manifest, edit,&max_sequence);if (!s.ok()) {return s;}// 記錄哪些文件編號已經被使用(可能回放的日志文件編號大于versions_的當前最大文件編號)versions_->MarkFileNumberUsed(logs[i]);}// 更新最大的全局事務序列號,因為log文件對應的memtable還沒生成sst,便不會被寫入到MANIFEST中。if (versions_->LastSequence() < max_sequence) {versions_->SetLastSequence(max_sequence);}return Status::OK(); }DBImpl::RecoverLogFile()
該函數打開指定的log文件,回放日志,用于恢復db。期間可能會執行compaction,生產新的level 0sstable文件,記錄文件變動到edit中。
它聲明了一個局部類LogReporter以打印錯誤日志
VersionSet::Recover
當正常運行期間,每當調用LogAndApply的時候,都會將VersionEdit作為一筆記錄,追加寫入到MANIFEST文件。我們知道VersionEdit就是記錄數據庫的一個版本到另一個版本間的sst文件變化情況以及各層合并點變化情況。
注意,VersionEdit可以序列化,存進MANIFEST文件,同樣道理,MANIFEST中可以將VersionEdit一個一個的重放出來。這個重放的目的,是為了得到當前的Version 以及VersionSet。
一般來講,當打開的DB的時候,需要獲得這種信息,而這種信息的獲得,靠的就是所有VersionEdit 按照次序一一回放,生成當前的Version。
總結
以上是生活随笔為你收集整理的leveldb:数据库recover机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅议精益生产方式的新发展阶段
- 下一篇: 一个=电视+私人影院+KTV+游戏厅,爽