数据快照是ZooKeeper数据存储中另一个非常核心的运行机制。顾名思义,数据快照用来记录ZooKeeper服务器上某一个时刻的全量内存数据内容,并将其写入到指定的磁盘文件中。

文件存储

        和事务日志类似,ZooKeeper的快照数据也使用特定的磁盘目录进行存储,可以通过dataDir属性进行配置。

        假设我们确定dataDir为/home/admin/zkData/zk_data,那么ZooKeeper在运行过程中会在该目录下建立一个名为version-2的目录,该目录确定了当前ZooKeeper使用的快照数据格式版本号。运行一段时间后,我们可以发现在/home/admin/zkData/zk_data/version-2目录下会生成类似下面这样的文件:

        和事务日志文件的命名规则一致,快照数据文件也是使用ZXID的十六进制表示来作为文件名后缀,该后缀标识了本次数据快照开始时刻的服务器最新ZXID。这个十六进制的文件名后缀非常重要,在数据恢复阶段,ZooKeeper会根据该ZXID来确定数据恢复的起始点。

         和事务日志文件不同的是,ZooKeeper的快照数据文件没有采用“预分配”机制,因此不会像事务日志文件那样内容中可能包含大量的“0”.每个快照数据文件中的所有内容都是有效地,因此该文件的大小在一定程度上能够反映当前ZooKeeper内存中全量数据的大小。

存储格式

        现在我们来看快照数据文件的内容。我们部署一个全新的ZooKeeper服务器,并进行一系列简单的操作,这个时候就会生成相应的快照数据文件,使用二进制编辑器将这个文件打开后,文件内容大体如下图所示。

        上图就是一个典型的数据快照文件内容,可以看出,ZooKeeper的数据快照文件同样让人无法看明白究竟文件内容是什么。所幸ZooKeeper也提供了一套简易的快照数据格式化工具org.apache.zookeeper.server.SnapshotFormatter,用于将这个默认的快照数据文件转化成可视化的数据内容,使用方法如下:

  • Java SnapshotFormatter 快照数据文件

        例如,我们针对执行上述系列操作之后产生的快照数据文件,执行以下代码:

java SnapshotFormatter snapshot.300000007

        执行后的输出结果如下图所示。

        从上图中我们可以看到,之前的二进制形式的文件内容已经被格式化输出了:SnapshotFormatter会将ZooKeeper上的数据节点逐个依次输出,但是需要注意的一点是,这里输出的仅仅是每个数据节点的元信息,并没有输出每个节点的数据内容,但这已经对运维非常有帮助了。

数据快照

        FileSnap负责维护快照数据对外的接口,包括快照数据的写入和读取等。我们首先来看数据的写入过程——将内存数据库写入快照数据文件中其实是一个序列化过程。

        ZooKeeper会在进行若干次事务日志记录之后,将内存数据库的全量数据Dump到本地文件中,这个过程就是数据快照。可以使用snapCount参数来配置每次数据快照之间的事务操作次数,即ZooKeeper会在snapCount参数来配置每次数据快照之间的事务操作次数,即ZooKeeper会在snapCount次事务日志记录后进行一个数据快照。下面我们重点来看数据快照的过程。

确定是否需要进行数据快照

        每进行一次事务日志记录之后,ZooKeeper都会检查当前是否需要进行数据快照。理论上进行snapCount次事务操作后就会开始数据快照,但是考虑到数据快照对于ZooKeeper所在机器的整体性能的影响,需要尽量避免ZooKeeper集群中的所有机器在同一时刻进行数据快照。因此ZooKeeper在具体的实现中,并不是严格的按照这个策略执行的,而是采取“过半随机”策略,即符合如下条件就进行数据快照:

        其中logCount代表了当前已经记录的事务日志数量,randRoll为1~snapCount/2之间的随机数,因此上面的条件就相当于:如果我们配置的snapCount值为默认的100000,那么ZooKeeper会在50000~100000次事务日志记录后进行一次数据快照。

切换事务日志文件

        满足上述条件之后,ZooKeeper就要开始进行数据快照了。首先是进行事务日志文件的切换。所谓的事务日志文件切换是指当前的事务日志已经“写满”(已经写入了snapCount个事务日志),需要重新创建一个新的事务日志。

创建数据快照异步线程

        为了保证数据快照过程不影响ZooKeeper的主流程,这里需要创建一个单独的异步线程来进行数据快照。

获取全量数据和会话信息

        数据快照本质上就是将内存中所有数据节点信息(DataTree)和会话信息保存到本地磁盘中去。因此这里会先从ZKDatabase中获取到DataTree和会话信息。

生成快照数据文件名

        在“文件存储”部分,我们已经提到快照数据文件名的命名空间。在这一步中,ZooKeeper会根据当前已提交的最大ZXID来生成数据快照文件名。

数据序列化

        接下来就开始真正的数据序列化了。在序列化时,首先会序列化文件头信息,这里的文件头和事务日志中的一致,同样也包含了魔数、版本号和dbid信息。然后再对会话信息和DataTree分别进行序列化,同时生成一个Checksum,一并写入快照数据文件中去。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐