Leveldb源码分析8 Snapshot原理

Snapshot(快照)关于指定数据集合的一个完全可用拷贝,该拷贝包括相应数据在某个时间点(拷贝开始的时间点)的映像。快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。

快照的作用主要是能够进行在线数据备份与恢复[0]

leveldb也提供了快照。对于整个key-value存储状态,Snapshot提供了一致性只读视图。ReadOptions::snapshot不为NULL时表示读取数据库的某一个特定版本。如果Snapshot为NULL,则读取数据库的当前版本。

通常Snapshots通过方法DB::GetSnapshot()创建:

  leveldb::ReadOptions options;
  options.snapshot = db->GetSnapshot();
  ... apply some updates to db ...
  leveldb::Iterator* iter = db->NewIterator(options);
  ... read using iter to view the state when the snapshot was created ...
  delete iter;
  db->ReleaseSnapshot(options.snapshot);

注意,当snapshot不再使用时,需要用DB::RealeaseSnapshot释放。这样底层就可以释放支持Snapshot的资源。

写操作也可以返回一个应用了一系列更新之后的Snapshot:

  leveldb::Snapshot* snapshot;
  leveldb::WriteOptions write_options;
  write_options.post_write_snapshot = &snapshot;
  leveldb::Status status = db->Write(write_options, ...);
  ... perform other mutations to db ...
 
  leveldb::ReadOptions read_options;
  read_options.snapshot = snapshot;
  leveldb::Iterator* iter = db->NewIterator(read_options);
  ... read as of the state just after the Write call returned ...
  delete iter;
 
  db->ReleaseSnapshot(snapshot);

leveldb中的Snapshot是如何实现的呢?

我们知道leveldb是指增加的数据库[1], 在一定的时间范围内leveldb并不删除数据,它记录了所有的操作。比如:

table["liming"] = 18
del table["liming"]
 
table["wangdong"] = 85
table["wangdong"] = 30

转换成leveldb内部操作为:

liming 1 kTypeValue     : 18
liming 2 kTypeDeletion
wandong 3 ktypeValue    : 85
wandong 4 kTypeValue    : 30

内部按照key非递减,sequence非递增,kTypeValue非递增排序(保证kTypeDeletion在前面)进行排序(存储在SkipList中),那么在这些数据在skiplist中为:[2]

//userkey sequence type : value
liming 2 kTypeDeletion
liming 1 kTypeValue     : 18
 
wandong 4 kTypeValue    : 30
wandong 3 ktypeValue    : 85

这时我们获得了快照号4(最后更新的sequence号),我们只读取seqence<=4的元素,如果查找table["wandong"], 那么找到 wandong 4 kTypeValue : 30
可以获得table["wandong"] 为 30,我们只取sequence最大的值作为数据的最新状态(自动忽略wandong 3 ktypeValue : 85), 如果查找table["liming"],找到liming的最新数据liming 2 kTypeDeletion,表示已经删掉了liming,则数据库中没有liming'.

如果之后又有更新比如:

table["liming"] = 19
table["wandong"]= 21
table["jim"] = 30

这时skiplist中的内容变为:

liming 5 kTypeValue     : 19
liming 2 kTypeDeletion
liming 1 kTypeValue     : 18
 
jim    7 kTypeValue     : 30
 
wandong 6 kTypeValue    : 21
wandong 4 kTypeValue    : 30
wandong 3 ktypeValue    : 85

这时我们查找table["liming"], 由于我们指读取sequence <= 4的数据所以我们会自动忽略新插入的元素liming 5 kTypeValue : 19', jim 7 kTypeValue : 30,wandong 6 kTypeValue : 21, 所以我们snapshot为4时,数据库中仍没有liming, table["wandong"] 为 30.

另外LevelDb中的SkipList中的指针为AtomicPointer,对它的读写操作是原子的。多线程写需要在外部加锁,因此最多只有一个线程写SkipList, 但是多个线程可以不用加锁并发的读取SkipList,Snapshot不关心新插入的key-value(因为他们的sequence>snapshot的sequence编号)。线程写SkipList的时候,仍然可以多线程读SkipList,并不会有问题。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值