Zookeeper数据与存储
数据与存储
1.内存数据
Zookeeper的数据模型是树结构,在内存数据库中,存储了整棵树的内容,Zookeeper会定时将这个数据存储到磁盘上。
(1)DataTree
DataTree是内存数据存储的核心,是一个树结构,代表了内存中一份完整的数据。DataTree不包含任何与网络、客户端连接及请求处理相关的业务逻辑,是一个独立的组件。
(2)DataNode
DataNode是数据存储的最小单元,其内部除了保存了结点的数据内容、ACL列表、节点状态之外,还记录了父节点的引用子节点列表两个属性,其他提供了对子节点列表进行操作的接口。
(3)ZKDatabase
Zookeeper的内存数据库,管理Zookeeper的所有会话、DataTree存储和事务日志。ZKDatabase会定进向磁盘dump快照数据,同时在Zookeeper启动时,会通过磁盘的事务日志和快照文件恢复一个完整的内存数据库。
2.事务日志
(1)文件存储
在配置Zookeeper集群时需要配置dataDir目录,其用来存储事务日志文件。也可以为事务日志单独分配一个文件存储目录:dataLogDir。若配置dataLogDir为/home/admin/zkData/zk_log,那么zookeeper在运行过程中会在该目录下建立一个名字为version-2的子目录,该目录确定了当前zookeeper使用的事务日志格式版本号,当下次某个Zookeeper版本对事务日志格式进行变更时,此目录也会变更。
(2)日志格式
在配置好日志文件目录,启动Zookeeper后,完成如下操作
- 创建/test_log节点,初始值为V1.
- 更新/test_log节点的数值为V2.
- 创建/test_log/c节点,初始值为V1
- 删除/test_log/c节点
经过四步操作后,会在/log/version-2/目录下生成一个日志文件log.cec。将Zookeeper下的zookeeper-3.4.6.jar的slf4j-api-1.6.1.jar复制到/log/version-2目录下,使用如
java -classpath ./zookeeper-3.4.6.jar:./slf4j-api-1.6.1.jar org.apache.zookeeer.server.LogFormatter log.cec命令打开log.cec文件。
(3)日志写入
FileTxnLog负责维护事务日志对外的接口。
- 确定是否有事务日志可写。当Zookeeper服务器启动完成需要进行第一次事务日志的写入,或是上一次事务日志写满时,都会处于与事务日志文件断开的状态,因此在进行事务日志写入前,Zookeeper首先会判断FileTxnLog组件是否已经关联上一个可写的事务日志文件。若没有,则会使用该事务操作关联的ZXID作为后缀创建一个事务日志文件,同时构建事务日志的文件头信息,并立即写入这个事务日志文件中去,同时将该文件的文件流放入streamToFlush集合,该集合用来记录当前需要强制进行数据落盘的文件流。
- 确定事务日志文件是否压需要扩容。Zookeeper会采用磁盘空间预分配策略。当检测到当前事务日志文件剩余不足4096字节时,就会开始进行文件空间扩容,即在现有文件大小上,将文件增加64MB,然后使用“0”填充被扩容的文件空间。
- 事务序列化。对事务头和事务体的序列化。
- 生成checksum。为保证日志文件的完事性和数据的准确性,zookeeper在将事务日志写入文件前,会计算生成checksum。
- 写入事务日志文件流。将序列化后的事务头、事务体和checksum写入文件流中
- 事务日志刷入磁盘。
(4)日志截断
在zookeeper运行过程中,可能出现非Leader记录的事务ID比Leader上大,这是非法运行状态。此时,需要保证所有机器必须与该Leader的数据保持同步,即Leader会发送TRUNC命令给该机器,要求进行日志截断,Learner收到该命令后,就会删除所有包含或大于该事务ID的事务日志文件。
3.snapshot
数据快照是Zookeeper数据存储中非常核心的运行机制,数据快照用来记录Zookeeper服务器上某一时刻的全量内存数据内容,并将其写入指定的磁盘文件中。
(1)文件存储
与事务文件类似,Zookeeper快照文件也可以指定磁盘目录,通过dataDir属性来配置。
(2)数据快照
FileSnap负责维护快照数据对外的接口。
- 确定是否需要进行数据快照。每进行一次事务日志记录之后,Zookeeper都会检测当前是否需要进行数据快照,考虑到数据快照对于Zookeeper机器的影响,需要尽量避免Zookeeper集群中的所有机器在同一时刻进行数据快照。采用过半随机策略进行数据快照操作。
- 切换事务日志文件。表示当前的事务日志已经写满,需要重新创建一个新的事务日志。
- 创建数据快照异步线程。以避免影响Zookeeper主流程。
- 获取全量数据和会话信息。从ZKDatabase获取到DataTreee和会话信息。
- 生成快照数据文件名。Zookeeper根据当前已经提交的最大ZXID来生成数据快照文件名。
- 数据序列化。首先序列文件头信息,然后再对会话信息和DataTree分别进行序列化,同时生成一个checksum,一并写入快照数据文件中去。
4.初始化流程
数据的初始化工作是从磁盘上加载数据的过程,主要包括从快照文件加载快照和根据事务日志进行数据修正两个过程。
(1)初始化FileTxnSnapLog,FileTxnSnapLog是Zookeeper事务日志和快照数据访问层,用于衔接上层业务和底层数据存储。
(2)初始化ZKDatabase。首先构建DataTree
(3)创建playbacklistener。在Zookeeper数据恢后期,会有事务修正过程,此过程会回调playbacklistener来进行对应的数据修正。
(4)处理快照文件。
(5)获取最新的100个快照文件。
(6)解析快照文件,逐个解析快照文件,此时需要进行反序列化,生成DataTree和SessionsWithTimeouts,同时还会检验Checksum及快照文件的正确性。通常只会解析最新的正确的快照文件。若100个快照文件解析完后还是无法成功恢复一个完整的DataTree和sessionWithTimeouts,此时服务器启动失败。
(7)获取最新的ZXID,此时根据快照文件的文件名即可解析出最新的ZXID:zxid_for_snap。
(8)处理事务日志。此时服务器内存中已经有了一份近似全量的数据,现在开始通过事务日志来更新新增数据。
(9)获取所有zxid_for_snap之后提交的事务。
(10)事务应用。获取大于zxid_for_snap的事务后,将其逐个应用到之前基于快照数据文件恢复出来DataTree和sessionWithTimeous。每当有一个事务被应用到内存数据库中后,Zookeeper同时会回调PlayBackListener,将这事务操作记录转换成Proposal,并保存到ZKDatabase和committedLog中,以便Follower进行快速同步。
(11)获取最新的ZXID。
(12)检验epoch。epoch标识了当前Leader周期,集群机器相互通信时,会带上这个epoch以确保彼此在同一个Leader周期中。完成数据加载后,Zookeeper会从步骤11中确定ZXID中解析出事务处理的Leader周期,同进也会从磁盘的currentEpoch和acceptedEpoch文件中读取上次记录的最新epoch值,进行检验。
5.数据同步
(1)获取Learner状态。在注册Learner的最后阶段,learner服务器会发送给Leader服务器一个ACKepcoh数据包,Leader会从这个数据包中解析出该learner的currentEpoch和lastzxid。
(2)数据同步初始化。首先从Zookeeper内存数据库中提取出事务请求对应的提议缓存队列proposals,同时完成peerLastZxid(该Learner最后处理的ZXID)、minCommittedLog(Leader提议缓存队列commitedLog中最小的zxid)、maxCommittedLog(Leader提议缓存队列commitedLog中的最大zxid)三个zxid值的初始化。
最后欢迎大家访问我的个人网站:1024s