Backup API
c++ api,請(qǐng)參考:include/rocksdb/utilities/backupable_db.h。核心模塊是backup engine,其暴露了創(chuàng)建backup的簡(jiǎn)單接口、查詢backup的相關(guān)信息以及從backup中恢復(fù)數(shù)據(jù)。backup engine主要有以下形式:1) 創(chuàng)建backup的BackupEngine 2)從backup恢復(fù)數(shù)據(jù)的BackupEngineReadOnly。每個(gè)engine都可以用來查詢backup的信息。
Creating and verifying a backup
RocksDB實(shí)現(xiàn)了一種backup DB的簡(jiǎn)單方式,還可以對(duì)backup做正確性校驗(yàn)。下面是一個(gè)簡(jiǎn)單示例:
#include "rocksdb/db.h"
#include "rocksdb/utilities/backupable_db.h"
#include <vector>
using namespace rocksdb;
int main() {
Options options;
options.create_if_missing = true;
DB* db;
Status s = DB::Open(options, "/tmp/rocksdb", &db);
assert(s.ok());
db->Put(...); // do your thing
BackupEngine* backup_engine;
s = BackupEngine::Open(Env::Default(), BackupableDBOptions("/tmp/rocksdb_backup"), &backup_engine);
assert(s.ok());
s = backup_engine->CreateNewBackup(db);
assert(s.ok());
db->Put(...); // make some more changes
s = backup_engine->CreateNewBackup(db);
assert(s.ok());
std::vector<BackupInfo> backup_info;
backup_engine->GetBackupInfo(&backup_info);
// you can get IDs from backup_info if there are more than two
s = backup_engine->VerifyBackup(1 /* ID */);
assert(s.ok());
s = backup_engine->VerifyBackup(2 /* ID */);
assert(s.ok());
delete db;
delete backup_engine;
}
??例子中會(huì)對(duì)/tmp/rocksdb_backup數(shù)據(jù)創(chuàng)建兩個(gè)backup。用戶可以使用相同的backup engine來創(chuàng)建和校驗(yàn)多個(gè)backup。
??正常情況下,backup數(shù)據(jù)是遞增的(參考BackupableDBOptions::share_table_files)。用戶可以使用BackupEngine::CreateNewBackup() 創(chuàng)建一個(gè)新的backup,且只有新增的數(shù)據(jù)才會(huì)copy到backup 目錄中。
??如果你已經(jīng)保存了一些backup,就可以調(diào)用BackupEngine::GetBackupInfo()來查詢所有的backup信息。每一個(gè)backup都有一個(gè)遞增的ID來標(biāo)識(shí)。
??當(dāng)調(diào)用BackupEngine::VerifyBackups()時(shí),會(huì)check backup目錄中文件的大小,并與db目錄中相應(yīng)的文件對(duì)比。但是,并不會(huì)對(duì)文件的checksum進(jìn)行校驗(yàn),因?yàn)檫@樣需要讀取所有的文件數(shù)據(jù)。調(diào)用BackupEngine::VerifyBackups()接口進(jìn)行校驗(yàn)唯一適用的場(chǎng)景就是在執(zhí)行完create backup之后,因?yàn)樾r?yàn)邏輯中使用了backup期間的一些狀態(tài)信息。
Restoring a backup
#include "rocksdb/db.h"
#include "rocksdb/utilities/backupable_db.h"
using namespace rocksdb;
int main() {
BackupEngineReadOnly* backup_engine;
Status s = BackupEngineReadOnly::Open(Env::Default(), BackupableDBOptions("/tmp/rocksdb_backup"), &backup_engine);
assert(s.ok());
backup_engine->RestoreDBFromBackup(1, "/tmp/rocksdb", "/tmp/rocksdb");
delete backup_engine;
}
示例中會(huì)把第一個(gè)backup數(shù)據(jù)恢復(fù)到/tmp/rocksdb。BackupEngineReadOnly::RestoreDBFromBackup()的第一個(gè)參數(shù)是backup ID,第二個(gè)參數(shù)是目標(biāo)DB目錄,第三個(gè)參數(shù)是日志文件的目標(biāo)位置目錄(DB目錄和LOG目錄可以不一致)。BackupEngineReadOnly::RestoreDBFromLatestBackup()接口會(huì)從最新的backup(ID最大的backup)中恢復(fù)數(shù)據(jù)到DB?;謴?fù)期間,會(huì)計(jì)算所有存儲(chǔ)文件的checksum,并與backup期間存儲(chǔ)的checksum值對(duì)比。如果檢測(cè)到checksum不匹配,就會(huì)中斷restore過程,返回Status::Corruption。如果要查詢恢復(fù)的數(shù)據(jù),需要打開所有恢復(fù)成功的數(shù)據(jù)庫(kù)。
Backup directory structure
/tmp/rocksdb_backup/
├── LATEST_BACKUP
├── meta
│ └── 1
├── private
│ └── 1
│ ├── CURRENT
│ ├── MANIFEST-000008
| └── OPTIONS-000009
└── shared_checksum
└── 000007_1498774076_590.sst
??LATEST_BACKUP是一個(gè)記錄了最大backup id的文件。這個(gè)文件用來查詢最大的backup id,由于META信息中也包含了這個(gè)最大id,所以在RocksDB 5.0版本之后就刪除了這個(gè)文件。
??meta目錄包含了meta文件,對(duì)應(yīng)地記錄了每一個(gè)backup的描述信息,文件名就是backup id。如果有三個(gè)backup分別為1、2、3,那么這個(gè)目錄中就包含了文件名為1、2、3的文件,記錄了對(duì)應(yīng)backup的信息。
??private目錄有non-SST 文件,主要是options,current,manifest和WAL。如果不設(shè)置Options::share_table_files,那么SST file也會(huì)在這個(gè)目錄中。
??shared目錄包含了SSTfile(前提是設(shè)置了Options::share_table_files且Options::share_files_with_checksum未設(shè)置)。在這個(gè)目錄中文件與db目錄中的文件名相同。所以所以,在這種情況下,只能備份單個(gè)RocksDB實(shí)例,否則文件名會(huì)沖突。
??shared_checksum目錄中包含了SST files(前提是設(shè)置了Options::share_table_files和Options::share_files_with_checksum)。文件名以DB目錄中的文件名、size和checksum組成。這個(gè)文件名是唯一的,可以來自不同的RocksDB實(shí)例。
Backup performance
??backup engine的open函數(shù)的耗時(shí)與當(dāng)前存在的backup的數(shù)目是正相關(guān)的,因?yàn)槲覀円猧nitialize所有backup 的信息。如果backup數(shù)據(jù)是在remote文件系統(tǒng)(比如HDFS)且有很多backup,那么初始化backup engine會(huì)消耗一額外的網(wǎng)絡(luò)傳輸時(shí)間。官方建議backup engine一直保持打開狀態(tài),不需要在每一次backup或者restore時(shí)都重新創(chuàng)建。
??另一種加速backup engine 初始化的方法就是刪除非必須的backup。可以通過調(diào)用PurgeOldBackups(N)函數(shù)要?jiǎng)h除backups,其中N表示要保留多少個(gè)backup。除了top N newest backups保留外,其他的都被刪除。用戶也可以調(diào)用DeleteBackup(id)來刪除任一個(gè)backup。
??要知道,backup性能是由從Local db中讀數(shù)據(jù)的速度和拷貝到backup目錄的速度共同決定的。盡管用戶可以使用不同的環(huán)境來讀數(shù)據(jù)和拷貝數(shù)據(jù),但是仍然可能存在讀寫瓶頸。比如,如果local db是HDD的話,盡管配置了更多的線程來做backup,但是未必就會(huì)有效果。這是因?yàn)榇藭r(shí)的性能瓶頸是磁盤的讀性能,很有可能早就已經(jīng)飽和了。一個(gè)低配的HDFS集群也不能提供好的并行性能。但是,如果local db是SSD且backup目標(biāo)是在高性能的HDFS上的話,配置更多的線程往往會(huì)有收益。在RocksDB官方的benchmark里,配16個(gè)線程與單線程相比,前者的backup time是后者的1/3。
Under the hood
當(dāng)調(diào)用BackupEngine::CreateNewBackup()時(shí)會(huì)做以下工作:
- 1、禁止文件刪除
- 2、找到live files(table、current、options、manifest)
- 3、copy live files到backup 目錄
- 4、如果設(shè)置了flush_before_backup為false,需要拷貝log files到backup 目錄??梢哉{(diào)用GetSortedWalFiles()然后拷貝到備份目錄。
- 5、重新允許文件刪除
Advanced usage
??RocksDB支持將用戶自定義的metadata數(shù)據(jù)保存在backup中。調(diào)用BackupEngine::CreateNewBackupWithMetadata()函數(shù)可以創(chuàng)建backup并保存Metadata,后續(xù)可以調(diào)用BackupEngine::GetBackupInfo()來讀取Metadata。這可以用來根據(jù)backup id查詢meta信息來區(qū)分不同的backup。
??RocksDB也支持備份和恢復(fù)options file。在恢復(fù)數(shù)據(jù)后,可以調(diào)用ocksdb::LoadLatestOptions() or rocksdb:: LoadOptionsFromFile()接口來從db目錄中l(wèi)oad配置信息。有個(gè)限制就是,并不是options 對(duì)象中的每個(gè)配置都可以轉(zhuǎn)為text存儲(chǔ)在文件中,在restore結(jié)束后加載options時(shí),用戶需要手動(dòng)執(zhí)行一些步驟來加載這些特殊的option信息。
??備份時(shí),需要實(shí)例化一些環(huán)境變量和初始化BackupableDBOptions::backup_env。設(shè)置backup root dir(BackupableDBOptions::backup_dir)。在backup目錄中,文件會(huì)按照上述所示的結(jié)構(gòu)組織在一起。
- BackupableDBOptions::share_table_files
這個(gè)配置控制著多個(gè)backup是否是遞增完成的。如果設(shè)置為true的話,SST file都會(huì)存儲(chǔ)在shred/目錄下。如果不同的SST file具有相同名字的話,有可能會(huì)發(fā)生沖突(比如,多個(gè)數(shù)據(jù)庫(kù)有相同的backup目錄)。 - BackupableDBOptions::share_files_with_checksum
這個(gè)配置控制著shared files是怎么被識(shí)別的。如果設(shè)置為true的話,shared files是通過checksum、size和序列號(hào)來區(qū)分的。這樣如果多個(gè)數(shù)據(jù)庫(kù)配置了相同的backup目錄的話,就會(huì)避免文件名沖突問題。 - BackupableDBOptions::max_background_operations
這個(gè)參數(shù)配置了在backup和restore期間,有多少個(gè)線程用于copy文件。對(duì)于HDFS等分布式分解系統(tǒng),可以通過并行copy提高性能。 - BackupableDBOptions::info_log
是一個(gè)Logger object用來打印LOG 信息。
??如果BackupableDBOptions::sync設(shè)置為true的話,RocksDB會(huì)在每一次文件寫時(shí)調(diào)用fsync來同步文件數(shù)據(jù)和meta數(shù)據(jù)到磁盤,這樣可以保證如果服務(wù)器宕機(jī)后重啟的話,backups是滿足數(shù)據(jù)一致性的。如果設(shè)置為false的話,可能會(huì)提高一點(diǎn)點(diǎn)性能,但是會(huì)引起backup的不一致問題。盡管如下,絕大部分情況下,還是沒有問題的。
??如果設(shè)置了BackupableDBOptions::destroy_old_data為true的話,創(chuàng)建一個(gè)新的BackupEngine時(shí)會(huì)刪除所有老的backup數(shù)據(jù)。
??BackupEngine::CreateNewBackup() 方法會(huì)有一個(gè)flush_before_backup參數(shù),默認(rèn)為false。如果這個(gè)參數(shù)為true的話,BackupEngine會(huì)首先執(zhí)行一次memtable flush,然后只拷貝DB files 到backup 目錄,不會(huì)拷貝log files到backup目錄的原因是因?yàn)閒lush操作最終會(huì)觸發(fā)并刪除這些log file。如果這個(gè)參數(shù)設(shè)置為false的話,在開始backup時(shí),BackupEngine不會(huì)執(zhí)行flush操作。這種情況下,backup也會(huì)拷貝live memtable對(duì)應(yīng)的log files。不管這個(gè)參數(shù)為true還是false,backup都會(huì)和數(shù)據(jù)庫(kù)的當(dāng)前狀態(tài)保持一致性。