2018年7月2日 星期一

Google LevelDB 原始碼解說(五) Snapshot

Snapshot(快照)主要的目的是複製數據,複製相應數據的某個時間點的存檔,主要功能就是進行數據的備份跟回復,不知道讀者有沒有玩過RPG遊戲,會存多個時間的存檔,要是現行進行的遊戲出了問題,或是玩家想回到某個時間點,就可以讀取存檔,Snapshot有點類似這種概念。

Leveldb也有提供快照,提供一致性且唯讀。




Snapshot使用方式


在dbImpl類裡,調用GetSnapshot(),回傳SnapshotList類的new方法(下方會介紹),用上一個序列號生成一個快照,並插入快照鍊表中


  1. const Snapshot* DBImpl::GetSnapshot() {
  2. MutexLock l(&mutex_);
  3. return snapshots_.New(versions_->LastSequence());
  4. }
調用ReleaseSnapshot(const Snapshot* s),回傳Snapshotlist類Delete方法刪除當前快照
  1. void DBImpl::ReleaseSnapshot(const Snapshot* s) {
  2. MutexLock l(&mutex_);
  3. snapshots_.Delete(reinterpret_cast(s));
  4. }

Snapshot底層實作

Snapshots是抽象類別,在db.h中,而快照的實現為SnapshotImpl定義於snapshot.h中,而Snapshotlist是一個環狀鏈結串鍊的資料結構

  1. class SnapshotImpl : public Snapshot {
  2. public:
  3. SequenceNumber number_; // const after creation
  4. //保存當前快照的序列號
  5. private:
  6. friend class SnapshotList;
  7.  
  8. // SnapshotImpl is kept in a doubly-linked circular list
  9. SnapshotImpl* prev_;
  10. SnapshotImpl* next_;
  11.  
  12. SnapshotList* list_; // just for sanity checks
  13. };
  1. class SnapshotList {
  2. public:
  3. SnapshotList() {
  4. list_.prev_ = &list_;
  5. list_.next_ = &list_;
  6. }
  7. //這邊所提到的實現也就是剛剛上方Get與Release所使用的New與Delete方法
  8.  
  9. bool empty() const { return list_.next_ == &list_; }//判斷是否是空
  10. SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; }//取出最舊的快照
  11. SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; }//取出最新的快照
  12. //插入環狀鏈結
  13. const SnapshotImpl* New(SequenceNumber seq) {
  14. SnapshotImpl* s = new SnapshotImpl;
  15. s->number_ = seq;//儲存序列號
  16. s->list_ = this;//初始化
  17. s->next_ = &list_;//s的next指向串列
  18. s->prev_ = list_.prev_;//的prev指向原本串列的prev(也就是最後一個,因為頭的prev繞指向尾巴)
  19. s->prev_->next_ = s;//s的prev(也就是最後一個)的next指向s
  20. s->next_->prev_ = s;//s的next(原本串列的第一個)的prev指向s
  21. return s;
  22. }
  23. //刪除道理相反,這邊就不再次解釋
  24. void Delete(const SnapshotImpl* s) {
  25. assert(s->list_ == this);
  26. s->prev_->next_ = s->next_;
  27. s->next_->prev_ = s->prev_;
  28. delete s;
  29. }
  30.  
  31. private:
  32. // Dummy head of doubly-linked list of snapshots
  33. SnapshotImpl list_;
  34. };
這裡一個小程式示範snapshot的用法
# include <iostream>
# include <cassert>
# include <leveldb/db.h>
int  main ( void )
{
        leveldb::DB *db;
        leveldb::Options options;
        options.create_if_missing= true ;
        leveldb::Status status=leveldb::DB::Open(options, "player" ,&db);
        assert(status.ok());
        std :: string key1= "kobe" ;
        std :: string value1= "8" ;
        status=db->Put(leveldb::WriteOptions(),key1,value1);
        assert(status.ok());
        leveldb::ReadOptions readoptions;
        readoptions.snapshot=db->GetSnapshot();//儲存snapshot
        std :: string value2= "24" ;
        status=db->Put(leveldb::WriteOptions(),key1,value2);
        assert(status.ok());
        std :: string value;
        status=db->Get(readoptions,key1,&value);//記得要放入你創建的readoptions
        assert(status.ok());
        std :: cout <<value<< std :: endl ;
        db->ReleaseSnapshot(readoptions.snapshot);
        delete db;
        return 0;
}
輸出為Kobe的舊背號8

沒有留言:

張貼留言

熱門文章