- [3、数据同步](#3_265)
- * [(1)Learner 向 Leader 发送 ACKEPOCH](#1Learner__Leader__ACKEPOCH_269)
* [(2)Leader 初始化 peerLastZxid、minCommittedLog、maxCommittedLog](#2Leader__peerLastZxidminCommittedLogmaxCommittedLog_273)
* [(3.1)直接差异化同步(DIFF)](#31DIFF_283)
* [(3.2)先回滚再差异化同步(TRUNC+DIFF)](#32TRUNCDIFF_301)
* [(3.3)仅回滚同步(TRUNC)](#33TRUNC_320)
* [(3.4)全量同步(SNAP)](#34SNAP_328)
+ [六、数据备份与恢复流程](#_341)
+ - [1、为什么需要快照日志](#1_343)
- [2、数据备份策略](#2_349)
- [3、数据恢复](#3_359)
+ [七、要点总结](#_373)
+ [八、参考资料](#_389)
一、前言
ZooKeeper
存储数据的底层数据结构是LSM
(Log Structured Merge-tree
)。基于LSM
实现的存储引擎有两个显著特点:MVCC
(Multi-Version Concurrency Control
)和存储KV
,即所有的更新操作,都是先追加事务日志,然后把key-value
存储到内存数据库,注意不是replace
,而是保存对一个key-value
的多个版本。
ZooKeeper
的数据备份与恢复,就用到了其事务日志文件和快照日志文件。快照日志文件是什么概念?为什么要有快照日志文件?
如何做数据备份?如何恢复数据?解开这些疑惑,需要深入了解ZooKeeper
底层是如何保证数据一致性和集群启动时如何做初始化数据加载和同步。
二、ZooKeeper顺序一致性
ZooKeeper
专门设计了 Zab
(Zookeeper Atomic Broadcast
)协议作为其数据一致性协议。所有写操作和客户端会话管理都以事务方式由 Leader
统一协调,使用两阶段提交的方式,保证数据一致性。
1、ZooKeeper如何处理请求
客户端和 ZooKeeper
集群中的任一服务建立连接,即可发送请求,请求主要包括两类,只读请求和事务请求。
(1)只读请求本机处理
只读请求包括 getData
、getChildren
、exists
等,zk服务器接收到只读请求无需转发给 Leader
,可直接本机处理响应。
(2)事务请求转发给Leader协调
事务请求包括 create
、delete
、setData
以及客户端会话的创建和销毁(createSession
、closeSession
)。
Leader
收到事务请求可以直接协调处理,并发给Follower
做数据一致性同步;Follower
收到事务请求就需要先转发给 Leader
,由Leader
统一生成事务提议。
事务请求是原子性和幂等的。
2、两阶段提交+过半数机制
(1)第一阶段提交事务请求
第一阶段是投票阶段,主要是让集群中半数以上的服务持久化事务请求到事务日志文件。所有事务请求由 Leader
统一生成事务提议广播给 Follower
,具体流程如下:
Leader
首先将事务请求持久化到事务日志文件中。Leader
生成事务提议,将其广播给所有Follower
。Follower
收到事务提议,也将事务持久化到事务日志文件,并给Leader
回应一个ACK
消息,表示已做完事务持久化工作。
(2)第二阶段执行事务提交
第二阶段,Leader
收到半数以上的ACK
后,认为半数以上的服务都完成了事务日志持久化操作,可以继续将事务请求同步到内存数据库中,此为事务提交阶段。具体流程如下:
Leader
收到一半以上的ACK
信息后,进入事务commit
阶段,Leader
先将事务请求同步到内存数据库。- 如果是
Leader
自己收到的事务请求,此时就可以响应客户端了。 - Leader 向所有
Follower
广播 COMMIT 消息。 Follower
收到 COMMIT 后,将事务请求同步到内存数据库。- 如果是
Follower
收到的事务请求又转发给Leader,此时Follower
就可以响应客户端了。
如下图分别为 Leader
接收到事务请求和 Follower
接收事务请求的处理过程:
需要注意,假设集群有3个服务节点,Leader
先将事务写入日志文件,相当于已经有一个服务节点完成了第一阶段(就是 Leader
自己),然后广播事务提议给 Follower
,只要有一个 Follower
响应了ACK
,Leader
就可以进入第二阶段 COMMIT
,此时Leader
将事务同步到内存数据库,就可以响应客户端了。