level Snapshot源码分析
Posted 叶长风
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了level Snapshot源码分析相关的知识,希望对你有一定的参考价值。
# level Snapshot源码分析
上一篇文章中讲了WriteBatch,这一篇文章中开始讲快照Snapshot,Snapshot在文章中 leveldb中WriteBatch、Snapshot使用中讲过使用方法,这里面就不再继续讲用法了,这一节结合着之前写的Demo来讲源码。
Snapshot源码
之前写的demo为:
private final File databaseDir = FileUtils.createTempDir("leveldb");
private final DBFactory factory = Iq80DBFactory.factory;
File getTestDirectory(String name) throws IOException
File rc = new File(databaseDir, name);
factory.destroy(rc, new Options().createIfMissing(true));
rc.mkdirs();
return rc;
@Test
public void test01() throws IOException
Options options = new Options().createIfMissing(true).compressionType(CompressionType.NONE);
File path = getTestDirectory("testSnapshot");
DB db = factory.open(path, options);
WriteOptions writeOptions = new WriteOptions();
db.put(Iq80DBFactory.bytes("key"), Iq80DBFactory.bytes("value" + 1), writeOptions);
ReadOptions readOptions = new ReadOptions();
Snapshot snapshot = db.getSnapshot();
readOptions.snapshot(snapshot);
String value2 = "value2";
db.put(Iq80DBFactory.bytes("key"), Iq80DBFactory.bytes(value2), new WriteOptions());
byte[] value = db.get(Iq80DBFactory.bytes("key"), new ReadOptions());
System.out.println(Iq80DBFactory.asString(value));
byte[] value3 = db.get(Iq80DBFactory.bytes("key"), readOptions);
System.out.println(Iq80DBFactory.asString(value3));
snapshot.close();
db.close();
leveldb的快照主要功能是用来读取某个时间点之前的数据,因为leveldb在插入数据时,键值是可以一样的,所以当查询这个键值时,系统返回的是最新的数据,也就是后面插入的数据。但是如果在第二次插入相同键值数据之前,建立一个快照,那么读取这个快照时,读取的就是这个快照时间点之前的数据。
上述代码不再演示,我们直接来看看Snapshot源码,其实Snapshot中最关键的还是有一个lastSequence字段,部分源码如下:
private final AtomicBoolean closed = new AtomicBoolean();
private final Version version;
private final long lastSequence;
在leveldb快照中每次都是用一个序列号保存当前插入的这一条记录,因此当插入多条相同的记录时,通过序列号来确定那一条是最新的记录,在leveldb的快照中,在调用一个快照时,只要获取在当前快照序列号以下的记录,就可以读取到这个快照之前的数据,这也就是leveldb中快照的原理。
我们可以看下这一行代码:
db.get(Iq80DBFactory.bytes("key"), readOptions);
readOptions中保存了当前的快照,我们去看下get源码,如下:
SnapshotImpl snapshot = getSnapshot(options);
lookupKey = new LookupKey(Slices.wrappedBuffer(key), snapshot.getLastSequence());
// First look in the memtable, then in the immutable memtable (if any).
LookupResult lookupResult = memTable.get(lookupKey);
在拿到快照后,取出快照中的sequence number,根据传入的key和sequence number进文件中查找记录,这样就能查找快照之前的数据。
获取快照的方法为:
private SnapshotImpl getSnapshot(ReadOptions options)
SnapshotImpl snapshot;
if (options.snapshot() != null)
snapshot = (SnapshotImpl) options.snapshot();
else
snapshot = new SnapshotImpl(versions.getCurrent(), versions.getLastSequence());
snapshot.close(); // To avoid holding the snapshot active..
return snapshot;
就是取出之前保存在readOption中的Snapshot,没有太多特殊的地方。
在保存当前快照时,取出当前version与序列号生成快照并返回,如下:
public Snapshot getSnapshot()
checkBackgroundException();
mutex.lock();
try
return new SnapshotImpl(versions.getCurrent(), versions.getLastSequence());
finally
mutex.unlock();
快照在这里并不是简简单单的序列号,而是还有一个version对象,这个对象中保存了当前节点的文件元数据信息,level层级等等,这些都是在寻找快照之前数据时使用的。
下一节讲Snapshot中提到的Version、VersionSet。
以上是关于level Snapshot源码分析的主要内容,如果未能解决你的问题,请参考以下文章
《Elasticsearch 源码解析与优化实战》第13章:Snapshot 模块分析
《Elasticsearch 源码解析与优化实战》第13章:Snapshot 模块分析
9. SOFAJRaft源码分析— Follower如何通过Snapshot快速追上Leader日志?