Zookeeper详解(八):Zookeeper数据存储

zookeeper日志有三类:快照(虽然不是日志可是它是数据)、事务日志(记录每次操做)、zookeeper本身系统日志。第三个不属于数据类因此这里不作说明。
数据库


快照数据数组

Zookeeper在运行时会在内存中维护一个完整的数据,就像内存数据库同样。ZKDatabase就是Zookeeper的内存数据库,负载管理Zookeeper的会话、存储和事务日志。它会按期dump一份数据快照到硬盘上,在Zookeeper启动时根据这个快照数据和事务日志来加载一份完整的数据到内存。这一点跟Redis很像,其实不少时候思路都是同样。服务器

经过在配置文件中设置dataDir来指定快照保存位置。将内存数据库写入快照文件实际上是一个序列化的过程。快照文件保存只是每一个节点的元数据而非数据自己数据结构

那么每次快照间隔多久呢?异步

其实能够经过snapCount来进行配置,这个值得含义是每次快照之间的事务数量。也就是说执行多少次事务操做后进行一次快照。ide


  1. 每完成一次事务操做Zookeeper都会检查是否达到snapCount设置也就是来判断是否须要进行快照操做,由于快照自己对机器性能有影响,要避免集群中全部节点都进行快照性能

  2. 若是要进行快照操做,首先就须要对事务日志进行截断而后切换,因此事务日志写满不是以64M为标准而是以事务条数为标准的。spa

  3. 建立异步线程来执行快照操做(这一点又和Redis的bgsave同样)操作系统

  4. 从ZKDatabase中获取全量数据和会话,由于要保存内存全部数据节点信息和会话信息线程

  5. 生成快照名称,会根据已提交的最大ZXID来生成快照名称

  6. 数据序列化,首先会序列化文件头信息(魔数、版本、dbid信息),而后对会话信息和DataTree(Zookeeper内存数据的核心一个树形数据结构,表明内存完整数据)分别序列化,同时生成一个校验和,而后一块儿写入数据文件中。


事务日志

在配置文件中经过dataLogDir来配置事务日志路径,若是不配置默认保存在dataDir指定的路径下面。

Snip20180630_34.png

这些文件都是65M,实际上是64M。并且都是以log.开头后面是一个十六进制数字做为后缀。这个后缀是一个ZXID它是写入该日志的第一个事务的事务ID,这样能够达到快速定位某一事务的效果。


日志写入过程:

  1. 当须要写入事务日志的时候Zookeeper会判断它是否和一个可写入的事务日志相关联,若是关联就写入,若是没有则用该事务的事务ID来建立一个事务日志,同时将这个事务日志对应的文件流放入到一个集合中(streamsToFlush),这个集合中记录的是当前须要强刷数据到磁盘的文件流(由于操做系统一般有延迟写入机制,对于Linux系统强刷等于调用fsync)。

  2. 事务日志文件采用预先分配空间策略,这样为了保证单一事务日志文件所占用的磁盘块是连续的,这也是为了提升性能。当Zookeeper发现当前正在写入的事务日志文件空间不足4KB时,就会启动预先分配空间策略进行扩容。第一次使用事务日志或者事务日志达到切割条数(snapCount参数触发快照)会启动预先分配策略;其余时候只要发现当前使用的事务日志空余不足4KB就进行扩容,扩容时使用0进行填充。

  3. 确保日志文件空间够以后就须要对进行事务序列化操做,最终产生一个字节数组。主要对事务头(TxnHeader)和事务体(Record)进行序列化。

  4. 根据序列化后的字节数据计算一个校验和

  5. 将字节数组和校验写入到文件流中。

  6. 因为该事务日志的文件流在集合中,这时候就会从集合里面取出文件流强刷落盘。

初始化

Zookeeper启动期间要完成数据初始化也就是将完整的数据加载到内存。它会根据快照和事务日志来完成加载过程。快照保存的是数据的元数据(不包括节点数据)而事务日志记录的操做和数据,那么使用快照+事务日志的方式能够还原完整的数据。它会先解析快照文件用于恢复出一个DataTree,此时就能够解析出个最新的ZXID,而后再根据ZXID来经过事务日志进行数据填充。另外还须要获取大于ZXID以后的事务日志进行应用。当完成数据恢复之后就能够得到一个最新的ZXID,这ZXID就是上次最后一个提交的事务ID。


完成初始化以后而且若是在集群环境中,Leader和其余服务器还会有一个数据同步过程。

Leader服务器会提取三个值:peerLastZxid(其余角色服务器最后处理的ZXID)、minCommittedLog(Leader服务器当前最小的ZXID)、maxCommittedLog(Leader服务器当前最大的ZXID)。

  • 差别同步:若是其余角色服务器的peerLastZxid介于minCommittedLog、maxCommittedLog那么就使用差别化同步,这时候Leader就知道他和对端服务器差多少,而后把这个差别进行发送再提交就是Zookeeper的两阶段提交。

  • 先回滚在差别同步:另一种状况是在上一种状况下发生了意外原来的Leader要发送可是此时该Leader挂了,那么其他的会进行Leader选举,新的Leader产生后原来的Leader恢复了,那么显然他俩的ZXID有可能不一样,新的Leader的ZXID小,可是要保证Leader的权威因此其余ZXID比它大的都要回滚到和现有Leader同样的状态或者小于Leader的ZXID的状态,而后在进行差别同步。

  • 仅回滚:也就是Leader让其余服务器都回滚到和本身同样的最大ZXID上

  • 全量同步:peerLastZxid小于Leader上的minCommittedLog,其实就是Leader将所有内存数据给Learner。这种状况一般用于在现有集群中又增长了一台Zookeeper服务器。

相关文章
相关标签/搜索