程序员RocksDB调研和研究

RocksDB系列十一:How to backup RocksD

2018-07-19  本文已影响8人  薛少佳

Backup API

c++ api,请参考:include/rocksdb/utilities/backupable_db.h。核心模块是backup engine,其暴露了创建backup的简单接口、查询backup的相关信息以及从backup中恢复数据。backup engine主要有以下形式:1) 创建backup的BackupEngine 2)从backup恢复数据的BackupEngineReadOnly。每个engine都可以用来查询backup的信息。

Creating and verifying a backup

RocksDB实现了一种backup DB的简单方式,还可以对backup做正确性校验。下面是一个简单示例:

 #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;
    }

  例子中会对/tmp/rocksdb_backup数据创建两个backup。用户可以使用相同的backup engine来创建和校验多个backup。
  正常情况下,backup数据是递增的(参考BackupableDBOptions::share_table_files)。用户可以使用BackupEngine::CreateNewBackup() 创建一个新的backup,且只有新增的数据才会copy到backup 目录中。
  如果你已经保存了一些backup,就可以调用BackupEngine::GetBackupInfo()来查询所有的backup信息。每一个backup都有一个递增的ID来标识。
  当调用BackupEngine::VerifyBackups()时,会check backup目录中文件的大小,并与db目录中相应的文件对比。但是,并不会对文件的checksum进行校验,因为这样需要读取所有的文件数据。调用BackupEngine::VerifyBackups()接口进行校验唯一适用的场景就是在执行完create backup之后,因为校验逻辑中使用了backup期间的一些状态信息。

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;
    }

示例中会把第一个backup数据恢复到/tmp/rocksdb。BackupEngineReadOnly::RestoreDBFromBackup()的第一个参数是backup ID,第二个参数是目标DB目录,第三个参数是日志文件的目标位置目录(DB目录和LOG目录可以不一致)。BackupEngineReadOnly::RestoreDBFromLatestBackup()接口会从最新的backup(ID最大的backup)中恢复数据到DB。恢复期间,会计算所有存储文件的checksum,并与backup期间存储的checksum值对比。如果检测到checksum不匹配,就会中断restore过程,返回Status::Corruption。如果要查询恢复的数据,需要打开所有恢复成功的数据库。

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是一个记录了最大backup id的文件。这个文件用来查询最大的backup id,由于META信息中也包含了这个最大id,所以在RocksDB 5.0版本之后就删除了这个文件。
  meta目录包含了meta文件,对应地记录了每一个backup的描述信息,文件名就是backup id。如果有三个backup分别为1、2、3,那么这个目录中就包含了文件名为1、2、3的文件,记录了对应backup的信息。
  private目录有non-SST 文件,主要是options,current,manifest和WAL。如果不设置Options::share_table_files,那么SST file也会在这个目录中。
  shared目录包含了SSTfile(前提是设置了Options::share_table_files且Options::share_files_with_checksum未设置)。在这个目录中文件与db目录中的文件名相同。所以所以,在这种情况下,只能备份单个RocksDB实例,否则文件名会冲突。
  shared_checksum目录中包含了SST files(前提是设置了Options::share_table_files和Options::share_files_with_checksum)。文件名以DB目录中的文件名、size和checksum组成。这个文件名是唯一的,可以来自不同的RocksDB实例。

Backup performance

  backup engine的open函数的耗时与当前存在的backup的数目是正相关的,因为我们要initialize所有backup 的信息。如果backup数据是在remote文件系统(比如HDFS)且有很多backup,那么初始化backup engine会消耗一额外的网络传输时间。官方建议backup engine一直保持打开状态,不需要在每一次backup或者restore时都重新创建。
  另一种加速backup engine 初始化的方法就是删除非必须的backup。可以通过调用PurgeOldBackups(N)函数要删除backups,其中N表示要保留多少个backup。除了top N newest backups保留外,其他的都被删除。用户也可以调用DeleteBackup(id)来删除任一个backup。
  要知道,backup性能是由从Local db中读数据的速度和拷贝到backup目录的速度共同决定的。尽管用户可以使用不同的环境来读数据和拷贝数据,但是仍然可能存在读写瓶颈。比如,如果local db是HDD的话,尽管配置了更多的线程来做backup,但是未必就会有效果。这是因为此时的性能瓶颈是磁盘的读性能,很有可能早就已经饱和了。一个低配的HDFS集群也不能提供好的并行性能。但是,如果local db是SSD且backup目标是在高性能的HDFS上的话,配置更多的线程往往会有收益。在RocksDB官方的benchmark里,配16个线程与单线程相比,前者的backup time是后者的1/3。

Under the hood

当调用BackupEngine::CreateNewBackup()时会做以下工作:

Advanced usage

  RocksDB支持将用户自定义的metadata数据保存在backup中。调用BackupEngine::CreateNewBackupWithMetadata()函数可以创建backup并保存Metadata,后续可以调用BackupEngine::GetBackupInfo()来读取Metadata。这可以用来根据backup id查询meta信息来区分不同的backup。
  RocksDB也支持备份和恢复options file。在恢复数据后,可以调用ocksdb::LoadLatestOptions() or rocksdb:: LoadOptionsFromFile()接口来从db目录中load配置信息。有个限制就是,并不是options 对象中的每个配置都可以转为text存储在文件中,在restore结束后加载options时,用户需要手动执行一些步骤来加载这些特殊的option信息。
  备份时,需要实例化一些环境变量和初始化BackupableDBOptions::backup_env。设置backup root dir(BackupableDBOptions::backup_dir)。在backup目录中,文件会按照上述所示的结构组织在一起。

  如果BackupableDBOptions::sync设置为true的话,RocksDB会在每一次文件写时调用fsync来同步文件数据和meta数据到磁盘,这样可以保证如果服务器宕机后重启的话,backups是满足数据一致性的。如果设置为false的话,可能会提高一点点性能,但是会引起backup的不一致问题。尽管如下,绝大部分情况下,还是没有问题的。
  如果设置了BackupableDBOptions::destroy_old_data为true的话,创建一个新的BackupEngine时会删除所有老的backup数据。
  BackupEngine::CreateNewBackup() 方法会有一个flush_before_backup参数,默认为false。如果这个参数为true的话,BackupEngine会首先执行一次memtable flush,然后只拷贝DB files 到backup 目录,不会拷贝log files到backup目录的原因是因为flush操作最终会触发并删除这些log file。如果这个参数设置为false的话,在开始backup时,BackupEngine不会执行flush操作。这种情况下,backup也会拷贝live memtable对应的log files。不管这个参数为true还是false,backup都会和数据库的当前状态保持一致性。

上一篇下一篇

猜你喜欢

热点阅读