leveldb基于SequenceNumber实现了快照,做到读写隔离,数据库不断写入新数据的同时,仍然可以通过快照读取到旧数据;另一方面,多线程并发访问情况下也无需采取额外的同步措施就可做到安全的访问。
先来看Snapshot的定义:
// Abstract handle to particular state of a DB. // A Snapshot is an immutable object and can therefore be safely // accessed from multiple threads without any external synchronization. class Snapshot { protected: virtual ~Snapshot(); }; // Snapshots are kept in a doubly-linked list in the DB. // Each SnapshotImpl corresponds to a particular sequence number. 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_; } 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->prev_ = list_.prev_; s->prev_->next_ = 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_; };
SnapshotImpl实现本质上是包含一个SequenceNumber用于标识时间点,再以双向链表的形式组织起来,主要接口由SnapshotList(含有表头节点)提供,新节点从头部插入。
SnapshotImpl中next_和pre_指针与传统的双向链表中语义是不同的,快照链表按照新旧串连起来,新节点总从表头节点后面插入,实际快照节点中,pre_指向较旧节点方向,next_指向较新节点方向。
使用上基于DBImpl分别对SnapshotList的New和Delete接口进行封装以GetSnapshot和ReleaseSnapshot对外提供使用。