2018年7月2日 星期一

Google LevelDB 原始碼解說(五) Snapshot

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

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




Snapshot使用方式


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


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

Snapshot底層實作

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

class SnapshotImpl : public Snapshot {
 public:
  SequenceNumber number_;  // const after creation 
                           //保存當前快照的序列號
 private:
  friend class SnapshotList;

  // SnapshotImpl is kept in a doubly-linked circular list
  SnapshotImpl* prev_;
  SnapshotImpl* next_;

  SnapshotList* list_;                 // just for sanity checks        
};
class SnapshotList {
 public:
  SnapshotList() {
    list_.prev_ = &list_;
    list_.next_ = &list_;
  }
//這邊所提到的實現也就是剛剛上方Get與Release所使用的New與Delete方法

  bool empty() const { return list_.next_ == &list_; }//判斷是否是空
  SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; }//取出最舊的快照
  SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; }//取出最新的快照
//插入環狀鏈結
  const SnapshotImpl* New(SequenceNumber seq) {
    SnapshotImpl* s = new SnapshotImpl;
    s->number_ = seq;//儲存序列號
    s->list_ = this;//初始化
    s->next_ = &list_;//s的next指向串列
    s->prev_ = list_.prev_;//的prev指向原本串列的prev(也就是最後一個,因為頭的prev繞指向尾巴)
    s->prev_->next_ = s;//s的prev(也就是最後一個)的next指向s
    s->next_->prev_ = s;//s的next(原本串列的第一個)的prev指向s
    return s;
  }
//刪除道理相反,這邊就不再次解釋
  void Delete(const SnapshotImpl* s) {
    assert(s->list_ == this);
    s->prev_->next_ = s->next_;
    s->next_->prev_ = s->prev_;
    delete s;
  }

 private:
  // Dummy head of doubly-linked list of snapshots
  SnapshotImpl list_;
};
這裡一個小程式示範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

沒有留言:

張貼留言

熱門文章