原文地址:http://www.blogjava.net/sandy/archive/2012/03/13/leveldb5.html
所谓snapshot就是一个快照,我们可以从快照中读到旧的数据。
先写一个测试程序来看看snapshot的使用:
#include
<
iostream
>
#include " leveldb/db.h "
using namespace std;
using namespace leveldb;
int main() {
DB * db ;
Options op;
op.create_if_missing = true ;
Status s = DB::Open(op, " /tmp/testdb " , & db);
if (s.ok()){
cout << " create successfully " << endl;
s = db -> Put(WriteOptions(), " abcd " , " 1234 " );
if (s.ok()){
cout << " put successfully " << endl;
string value;
s = db -> Get(ReadOptions(), " abcd " , & value);
if (s.ok()){
cout << " get successfully,value: " << value << endl;
}
}
if (s.ok()){
string value;
const Snapshot * ss = db -> GetSnapshot();
ReadOptions rop;
db -> Put(WriteOptions(), " abcd " , " 123456 " );
db -> Get(rop, " abcd " , & value);
if (s.ok()){
cout << " get successfully,value: " << value << endl;
}
rop.snapshot = ss;
db -> Get(rop, " abcd " , & value);
if (s.ok()){
cout << " get from snapshot successfully,value: " << value << endl;
}
db -> ReleaseSnapshot(ss);
}
}
delete db;
return 0 ;
}
#include " leveldb/db.h "
using namespace std;
using namespace leveldb;
int main() {
DB * db ;
Options op;
op.create_if_missing = true ;
Status s = DB::Open(op, " /tmp/testdb " , & db);
if (s.ok()){
cout << " create successfully " << endl;
s = db -> Put(WriteOptions(), " abcd " , " 1234 " );
if (s.ok()){
cout << " put successfully " << endl;
string value;
s = db -> Get(ReadOptions(), " abcd " , & value);
if (s.ok()){
cout << " get successfully,value: " << value << endl;
}
}
if (s.ok()){
string value;
const Snapshot * ss = db -> GetSnapshot();
ReadOptions rop;
db -> Put(WriteOptions(), " abcd " , " 123456 " );
db -> Get(rop, " abcd " , & value);
if (s.ok()){
cout << " get successfully,value: " << value << endl;
}
rop.snapshot = ss;
db -> Get(rop, " abcd " , & value);
if (s.ok()){
cout << " get from snapshot successfully,value: " << value << endl;
}
db -> ReleaseSnapshot(ss);
}
}
delete db;
return 0 ;
}
程序运行的输出结果是:
create successfully
put successfully
get successfully,value: 1234
get successfully,value: 123456
get from snapshot successfully,value: 1234
put successfully
get successfully,value: 1234
get successfully,value: 123456
get from snapshot successfully,value: 1234
可以看出,即使在数据更新后,我们仍然可以从snapshot中读到旧的数据。
下面我们来分析leveldb中snapshot的实现。
SequenceNumber(db/dbformat.h)
SequenceNumber是leveldb很重要的东西,每次对数据库进行更新操作,都会生成一个新的SequenceNumber,64bits,其中高8位为0,可以跟key的类型(8bits)进行合并成64bits。
typedef uint64_t SequenceNumber;
// We leave eight bits empty at the bottom so a type and sequence#
// can be packed together into 64-bits.
static const SequenceNumber kMaxSequenceNumber =
((0x1ull << 56) - 1);
// We leave eight bits empty at the bottom so a type and sequence#
// can be packed together into 64-bits.
static const SequenceNumber kMaxSequenceNumber =
((0x1ull << 56) - 1);
SnapShot(db/snapshot.h),,可以看出snapshot其实就是一个sequence number
class
SnapshotImpl :
public
Snapshot {
public :
// 创建后保持不变
SequenceNumber number_;
private :
friend class SnapshotList;
// 双向循环链表
SnapshotImpl * prev_;
SnapshotImpl * next_;
SnapshotList * list_; // just for sanity checks
};
public :
// 创建后保持不变
SequenceNumber number_;
private :
friend class SnapshotList;
// 双向循环链表
SnapshotImpl * prev_;
SnapshotImpl * next_;
SnapshotList * list_; // just for sanity checks
};
创建snapshot:
const
Snapshot
*
DBImpl::GetSnapshot() {
MutexLock l( & mutex_);
return snapshots_.New(versions_ -> LastSequence());
}
MutexLock l( & mutex_);
return snapshots_.New(versions_ -> LastSequence());
}
删除snapshot:
void
DBImpl::ReleaseSnapshot(
const
Snapshot
*
s) {
MutexLock l( & mutex_);
snapshots_.Delete(reinterpret_cast < const SnapshotImpl *> (s));
}
MutexLock l( & mutex_);
snapshots_.Delete(reinterpret_cast < const SnapshotImpl *> (s));
}