zk中的数据在内存中是DataTree为数据结构存储的
快照是间隔时间段将整个DataTree持久化
快照是备份版本,所以并不是最新数据
接口所在位置org.apache.zookeeper.server.persistence
**
* snapshot interface for the persistence layer.
* implement this interface for implementing
* snapshots.
*/
public interface SnapShot {
/**
* 将数据反序列化
* deserialize a data tree from the last valid snapshot and
* return the last zxid that was deserialized
* @param dt the datatree to be deserialized into
* @param sessions the sessions to be deserialized into
* @return the last zxid that was deserialized from the snapshot
* @throws IOException
*/
long deserialize(DataTree dt, Map<Long, Integer> sessions) throws IOException;
/**
* 持久化datatree 和会话
* persist the datatree and the sessions into a persistence storage
* @param dt the datatree to be serialized
* @param sessions the session timeouts to be serialized
* @param name the object name to store snapshot into
* @param fsync sync the snapshot immediately after write
* @throws IOException
*/
void serialize(DataTree dt, Map<Long, Integer> sessions, File name, boolean fsync) throws IOException;
/**
* 查找最近的快照文件
* find the most recent snapshot file
* @return the most recent snapshot file
* @throws IOException
*/
File findMostRecentSnapshot() throws IOException;
/**
* 获取快照信息
* get information of the last saved/restored snapshot
* @return info of last snapshot
*/
SnapshotInfo getLastSnapshotInfo();
/**
* 关闭资源
* free resources from this snapshot immediately
* @throws IOException
*/
void close() throws IOException;
}
实现类
属性
File snapDir;
SnapshotInfo lastSnapshotInfo = null;
private volatile boolean close = false;
private static final int VERSION = 2;
private static final long dbId = -1;
private static final Logger LOG = LoggerFactory.getLogger(FileSnap.class);
public static final int SNAP_MAGIC = ByteBuffer.wrap("ZKSN".getBytes()).getInt();
public static final String SNAPSHOT_FILE_PREFIX = "snapshot";
方法
/**
* deserialize a data tree from the most recent snapshot
* @return the zxid of the snapshot
*/
public long deserialize(DataTree dt, Map<Long, Integer> sessions) throws IOException {
// we run through 100 snapshots (not all of them)
// if we cannot get it running within 100 snapshots
// we should give up
List<File> snapList = findNValidSnapshots(100);
if (snapList.size() == 0) {
return -1L;
}
File snap = null;
boolean foundValid = false;
for (int i = 0, snapListSize = snapList.size(); i < snapListSize; i++) {
snap = snapList.get(i);
LOG.info("Reading snapshot " + snap);
try (CheckedInputStream snapIS = SnapStream.getInputStream(snap)) {
InputArchive ia = BinaryInputArchive.getArchive(snapIS);
deserialize(dt, sessions, ia);
SnapStream.checkSealIntegrity(snapIS, ia);
if (dt.deserializeZxidDigest(ia)) {
SnapStream.checkSealIntegrity(snapIS, ia);
}
foundValid = true;
break;
} catch (IOException e) {
LOG.warn("problem reading snap file " + snap, e);
}
}
if (!foundValid) {
throw new IOException("Not able to find valid snapshots in " + snapDir);
}
dt.lastProcessedZxid = Util.getZxidFromName(snap.getName(), SNAPSHOT_FILE_PREFIX);
lastSnapshotInfo = new SnapshotInfo(dt.lastProcessedZxid, snap.lastModified() / 1000);
// compare the digest if this is not a fuzzy snapshot, we want to compare
// and find inconsistent asap.
if (dt.getDigestFromLoadedSnapshot() != null) {
dt.compareSnapshotDigests(dt.lastProcessedZxid);
}
return dt.lastProcessedZxid;
}
/**
* find the last (maybe) valid n snapshots. this does some
* minor checks on the validity of the snapshots. It just
* checks for / at the end of the snapshot. This does
* not mean that the snapshot is truly valid but is
* valid with a high probability. also, the most recent
* will be first on the list.
* @param n the number of most recent snapshots
* @return the last n snapshots (the number might be
* less than n in case enough snapshots are not available).
* @throws IOException
*/
private List<File> findNValidSnapshots(int n) throws IOException {
List<File> files = Util.sortDataDir(snapDir.listFiles(), SNAPSHOT_FILE_PREFIX, false);
int count = 0;
List<File> list = new ArrayList<File>();
for (File f : files) {
// we should catch the exceptions
// from the valid snapshot and continue
// until we find a valid one
try {
if (SnapStream.isValidSnapshot(f)) {
list.add(f);
count++;
if (count == n) {
break;
}
}
} catch (IOException e) {
LOG.info("invalid snapshot " + f, e);
}
}
return list;
}
/**
* deserialize the datatree from an inputarchive
* @param dt the datatree to be serialized into
* @param sessions the sessions to be filled up
* @param ia the input archive to restore from
* @throws IOException
*/
public void deserialize(DataTree dt, Map<Long, Integer> sessions, InputArchive ia) throws IOException {
FileHeader header = new FileHeader();
header.deserialize(ia, "fileheader");
if (header.getMagic() != SNAP_MAGIC) {
throw new IOException("mismatching magic headers " + header.getMagic() + " != " + FileSnap.SNAP_MAGIC);
}
SerializeUtils.deserializeSnapshot(dt, ia, sessions);
}
序列化函数
/**
* serialize the datatree and sessions
* @param dt the datatree to be serialized
* @param sessions the sessions to be serialized
* @param oa the output archive to serialize into
* @param header the header of this snapshot
* @throws IOException
*/
protected void serialize(
DataTree dt,
Map<Long, Integer> sessions,
OutputArchive oa,
FileHeader header) throws IOException {
// this is really a programmatic error and not something that can
// happen at runtime
if (header == null) {
throw new IllegalStateException("Snapshot's not open for writing: uninitialized header");
}
header.serialize(oa, "fileheader");
SerializeUtils.serializeSnapshot(dt, oa, sessions);
}
public static void serializeSnapshot(DataTree dt, OutputArchive oa, Map<Long, Integer> sessions) throws IOException {
HashMap<Long, Integer> sessSnap = new HashMap<Long, Integer>(sessions);
oa.writeInt(sessSnap.size(), "count");
for (Entry<Long, Integer> entry : sessSnap.entrySet()) {
oa.writeLong(entry.getKey().longValue(), "id");
oa.writeInt(entry.getValue().intValue(), "timeout");
}
dt.serialize(oa, "tree");
}
快照和事务日志的区别?
快照什么时候会用到?
snapshot.打头的文件存储在哪里?