ZooKeeper事务操作支持机制深度解析
-
- 一、ZooKeeper事务的基本概念
-
- 1.1 什么是ZooKeeper中的事务?
- 1.2 ZXID:事务的唯一标识
- 二、事务的核心处理流程
-
- 2.1 事务处理整体架构
- 2.2 事务处理的四个阶段
-
- 阶段1:预处理阶段(PrepRequestProcessor)
- 阶段2:事务提案阶段(ProposalRequestProcessor)
- 阶段3:持久化阶段(SyncRequestProcessor)
- 阶段4:提交阶段(FinalRequestProcessor)
- 三、多操作的原子性:multi命令
-
- 3.1 multi命令的作用
- 3.2 multi命令的原子性保证
- 四、版本控制与乐观锁
-
- 4.1 版本号的作用
- 4.2 基于版本的事务控制
- 五、事务的持久化存储
-
- 5.1 事务日志(Transaction Log)
- 5.2 数据快照(Snapshot)
- 六、事务的一致性和隔离性
-
- 6.1 顺序一致性
- 6.2 单一系统映像
- 6.3 与数据库事务的区别
- 七、总结
-
- 7.1 事务支持的核心机制
- 7.2 事务处理完整流程
- 7.3 一句话总结
|
🌺The Begin🌺点点关注,收藏不迷路🌺 |
摘要:在分布式系统中,"事务"通常指一组具有ACID(原子性、一致性、隔离性、持久性)特性的操作。ZooKeeper中的事务概念略有不同,它特指任何改变服务器状态的写操作,如节点创建、数据更新、节点删除等。本文将深入剖析ZooKeeper如何通过ZAB协议、版本控制、持久化存储等机制支持这些事务操作,并探讨其与数据库事务的区别与联系。
一、ZooKeeper事务的基本概念
1.1 什么是ZooKeeper中的事务?
在ZooKeeper中,能够改变服务器状态的操作称为事务操作,主要包括:
| 读操作 | getData、getChildren、exists | ❌ 非事务 |
| 写操作 | create、setData、delete | ✅ 事务 |
| 会话操作 | 创建会话、关闭会话 | ✅ 事务 |
| 多操作 | multi(多个操作组合) | ✅ 事务 |
每个事务会被分配一个全局唯一、单调递增的ZXID(ZooKeeper Transaction ID)。
1.2 ZXID:事务的唯一标识
// ZXID的结构:64位长整数
// 高32位:epoch(纪元)—— Leader的任期,每次新Leader选举后自增1
// 低32位:counter(计数器)—— 事务计数器,每处理一个事务自增1
public class Zxid {
private long epoch; // 高32位
private long counter; // 低32位
// 示例:0x100000001 表示 epoch=1, counter=1
}
ZXID的作用:
- 全局有序:所有事务的ZXID严格递增,标识事务发生的先后顺序
- 唯一标识:整个集群范围内唯一标识一个事务
- 断点恢复:用于数据同步时确定同步点
二、事务的核心处理流程
2.1 事务处理整体架构
#mermaid-svg-Bd5Ty9MYc6gYkhRw{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .error-icon{fill:#552222;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .marker.cross{stroke:#333333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bd5Ty9MYc6gYkhRw p{margin:0;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .cluster-label text{fill:#333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .cluster-label span{color:#333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .cluster-label span p{background-color:transparent;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .label text,#mermaid-svg-Bd5Ty9MYc6gYkhRw span{fill:#333;color:#333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .node rect,#mermaid-svg-Bd5Ty9MYc6gYkhRw .node circle,#mermaid-svg-Bd5Ty9MYc6gYkhRw .node ellipse,#mermaid-svg-Bd5Ty9MYc6gYkhRw .node polygon,#mermaid-svg-Bd5Ty9MYc6gYkhRw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .rough-node .label text,#mermaid-svg-Bd5Ty9MYc6gYkhRw .node .label text,#mermaid-svg-Bd5Ty9MYc6gYkhRw .image-shape .label,#mermaid-svg-Bd5Ty9MYc6gYkhRw .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .rough-node .label,#mermaid-svg-Bd5Ty9MYc6gYkhRw .node .label,#mermaid-svg-Bd5Ty9MYc6gYkhRw .image-shape .label,#mermaid-svg-Bd5Ty9MYc6gYkhRw .icon-shape .label{text-align:center;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .node.clickable{cursor:pointer;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .arrowheadPath{fill:#333333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bd5Ty9MYc6gYkhRw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bd5Ty9MYc6gYkhRw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bd5Ty9MYc6gYkhRw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .cluster text{fill:#333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .cluster span{color:#333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bd5Ty9MYc6gYkhRw rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .icon-shape,#mermaid-svg-Bd5Ty9MYc6gYkhRw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .icon-shape p,#mermaid-svg-Bd5Ty9MYc6gYkhRw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .icon-shape rect,#mermaid-svg-Bd5Ty9MYc6gYkhRw .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bd5Ty9MYc6gYkhRw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bd5Ty9MYc6gYkhRw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bd5Ty9MYc6gYkhRw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
ZooKeeper事务处理架构
写请求
非Leader
Leader
客户端
任意服务器
转发给Leader
开始事务处理
预处理阶段PrepRequestProcessor
事务提案阶段ProposalRequestProcessor
持久化阶段SyncRequestProcessor
提交阶段FinalRequestProcessor
响应客户端
2.2 事务处理的四个阶段
阶段1:预处理阶段(PrepRequestProcessor)
// ZooKeeper源码中的预处理逻辑
protected void pRequest(Request request) {
switch (request.type) {
case OpCode.setData:
SetDataRequest setDataRequest = new SetDataRequest();
pRequest2Txn(request.type, zks.getNextZxid(),
request, setDataRequest, true);
break;
// 其他事务操作类似
}
}
预处理阶段完成的工作:
阶段2:事务提案阶段(ProposalRequestProcessor)
Leader将事务以**提案(Proposal)**形式广播给所有Follower:
Follower3
Follower2
Follower1
Leader
Follower3
Follower2
Follower1
Leader
#mermaid-svg-M3mysdWGcvaBRf50{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-M3mysdWGcvaBRf50 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M3mysdWGcvaBRf50 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M3mysdWGcvaBRf50 .error-icon{fill:#552222;}#mermaid-svg-M3mysdWGcvaBRf50 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M3mysdWGcvaBRf50 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M3mysdWGcvaBRf50 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M3mysdWGcvaBRf50 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M3mysdWGcvaBRf50 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M3mysdWGcvaBRf50 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M3mysdWGcvaBRf50 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M3mysdWGcvaBRf50 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M3mysdWGcvaBRf50 .marker.cross{stroke:#333333;}#mermaid-svg-M3mysdWGcvaBRf50 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M3mysdWGcvaBRf50 p{margin:0;}#mermaid-svg-M3mysdWGcvaBRf50 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-M3mysdWGcvaBRf50 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-M3mysdWGcvaBRf50 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-M3mysdWGcvaBRf50 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-M3mysdWGcvaBRf50 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-M3mysdWGcvaBRf50 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-M3mysdWGcvaBRf50 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-M3mysdWGcvaBRf50 .sequenceNumber{fill:white;}#mermaid-svg-M3mysdWGcvaBRf50 #sequencenumber{fill:#333;}#mermaid-svg-M3mysdWGcvaBRf50 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-M3mysdWGcvaBRf50 .messageText{fill:#333;stroke:none;}#mermaid-svg-M3mysdWGcvaBRf50 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-M3mysdWGcvaBRf50 .labelText,#mermaid-svg-M3mysdWGcvaBRf50 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-M3mysdWGcvaBRf50 .loopText,#mermaid-svg-M3mysdWGcvaBRf50 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-M3mysdWGcvaBRf50 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-M3mysdWGcvaBRf50 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-M3mysdWGcvaBRf50 .noteText,#mermaid-svg-M3mysdWGcvaBRf50 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-M3mysdWGcvaBRf50 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-M3mysdWGcvaBRf50 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-M3mysdWGcvaBRf50 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-M3mysdWGcvaBRf50 .actorPopupMenu{position:absolute;}#mermaid-svg-M3mysdWGcvaBRf50 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-M3mysdWGcvaBRf50 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-M3mysdWGcvaBRf50 .actor-man circle,#mermaid-svg-M3mysdWGcvaBRf50 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-M3mysdWGcvaBRf50 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
收到过半ACK
PROPOSAL(ZXID)
PROPOSAL(ZXID)
PROPOSAL(ZXID)
写入事务日志
写入事务日志
写入事务日志
ACK
ACK
阶段3:持久化阶段(SyncRequestProcessor)
Follower收到提案后,会将事务写入磁盘事务日志:
// 事务日志写入逻辑
public class SyncRequestProcessor {
public void run() {
while (true) {
// 1. 从队列获取请求
Request request = queuedRequests.take();
// 2. 写入事务日志文件
zks.getZKDatabase().append(request);
// 3. 定期刷盘(可配置forceSync)
if (shouldForceSync()) {
zks.getZKDatabase().commit();
}
// 4. 发送ACK给Leader
sendAck(request);
}
}
}
阶段4:提交阶段(FinalRequestProcessor)
当Leader收到过半ACK后,执行提交:
public class CommitProcessor {
public void commit(Request request) {
// 1. 将事务应用到内存数据库
zks.getZKDatabase().processTxn(request);
// 2. 广播COMMIT给所有Follower
sendCommitToFollowers(request);
// 3. 响应客户端
sendResponse(request);
}
}
三、多操作的原子性:multi命令
3.1 multi命令的作用
ZooKeeper提供multi命令(也称为事务API),允许将多个操作打包成一个原子单元执行:
// Java API中的multi操作
public void atomicMultiOperation() throws Exception {
ZooKeeper zk = new ZooKeeper("localhost:2181", 5000, null);
// 创建多个操作
List<Op> ops = Arrays.asList(
Op.create("/parent", new byte[0],
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT),
Op.create("/parent/child1", "data1".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT),
Op.create("/parent/child2", "data2".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT),
Op.check("/parent", 0) // 版本检查
);
// 原子执行
List<OpResult> results = zk.multi(ops);
// 要么全部成功,要么全部失败
}
# 命令行中的multi操作
[zk: localhost:2181(CONNECTED) 0] multi
create /parent ""
create /parent/child1 "data1"
create /parent/child2 "data2"
check /parent 0
commit
# 以上四个操作作为一个原子单元执行
3.2 multi命令的原子性保证
ZooKeeper通过以下机制保证multi操作的原子性:
#mermaid-svg-Bc7PO1V32xSNrjB2{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bc7PO1V32xSNrjB2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bc7PO1V32xSNrjB2 .error-icon{fill:#552222;}#mermaid-svg-Bc7PO1V32xSNrjB2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bc7PO1V32xSNrjB2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bc7PO1V32xSNrjB2 .marker.cross{stroke:#333333;}#mermaid-svg-Bc7PO1V32xSNrjB2 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bc7PO1V32xSNrjB2 p{margin:0;}#mermaid-svg-Bc7PO1V32xSNrjB2 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bc7PO1V32xSNrjB2 .cluster-label text{fill:#333;}#mermaid-svg-Bc7PO1V32xSNrjB2 .cluster-label span{color:#333;}#mermaid-svg-Bc7PO1V32xSNrjB2 .cluster-label span p{background-color:transparent;}#mermaid-svg-Bc7PO1V32xSNrjB2 .label text,#mermaid-svg-Bc7PO1V32xSNrjB2 span{fill:#333;color:#333;}#mermaid-svg-Bc7PO1V32xSNrjB2 .node rect,#mermaid-svg-Bc7PO1V32xSNrjB2 .node circle,#mermaid-svg-Bc7PO1V32xSNrjB2 .node ellipse,#mermaid-svg-Bc7PO1V32xSNrjB2 .node polygon,#mermaid-svg-Bc7PO1V32xSNrjB2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bc7PO1V32xSNrjB2 .rough-node .label text,#mermaid-svg-Bc7PO1V32xSNrjB2 .node .label text,#mermaid-svg-Bc7PO1V32xSNrjB2 .image-shape .label,#mermaid-svg-Bc7PO1V32xSNrjB2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bc7PO1V32xSNrjB2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bc7PO1V32xSNrjB2 .rough-node .label,#mermaid-svg-Bc7PO1V32xSNrjB2 .node .label,#mermaid-svg-Bc7PO1V32xSNrjB2 .image-shape .label,#mermaid-svg-Bc7PO1V32xSNrjB2 .icon-shape .label{text-align:center;}#mermaid-svg-Bc7PO1V32xSNrjB2 .node.clickable{cursor:pointer;}#mermaid-svg-Bc7PO1V32xSNrjB2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bc7PO1V32xSNrjB2 .arrowheadPath{fill:#333333;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bc7PO1V32xSNrjB2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bc7PO1V32xSNrjB2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bc7PO1V32xSNrjB2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bc7PO1V32xSNrjB2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bc7PO1V32xSNrjB2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bc7PO1V32xSNrjB2 .cluster text{fill:#333;}#mermaid-svg-Bc7PO1V32xSNrjB2 .cluster span{color:#333;}#mermaid-svg-Bc7PO1V32xSNrjB2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bc7PO1V32xSNrjB2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bc7PO1V32xSNrjB2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bc7PO1V32xSNrjB2 .icon-shape,#mermaid-svg-Bc7PO1V32xSNrjB2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bc7PO1V32xSNrjB2 .icon-shape p,#mermaid-svg-Bc7PO1V32xSNrjB2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bc7PO1V32xSNrjB2 .icon-shape rect,#mermaid-svg-Bc7PO1V32xSNrjB2 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bc7PO1V32xSNrjB2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bc7PO1V32xSNrjB2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bc7PO1V32xSNrjB2 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
任一操作不可行
全部可行
客户端提交multi请求
Leader预处理
检查所有操作可行性
整体失败
分配单个ZXID
按顺序广播提案
过半确认后整体提交
关键点:
- 整个multi操作共享同一个ZXID
- 服务端会预先检查所有操作,任何一个失败则全部放弃
- 执行时按顺序执行,要么全部生效,要么全部不生效
四、版本控制与乐观锁
4.1 版本号的作用
每个ZNode维护三个版本号,用于实现乐观锁控制:
| dataVersion | 数据版本 | 每次setData操作+1 |
| cversion | 子节点版本 | 子节点新增/删除+1 |
| aversion | ACL版本 | 每次setAcl操作+1 |
4.2 基于版本的事务控制
public class VersionControlExample {
public void casUpdate(String path, String newData) throws Exception {
ZooKeeper zk = new ZooKeeper("localhost:2181", 5000, null);
// 1. 获取当前数据和版本
Stat stat = new Stat();
byte[] data = zk.getData(path, false, stat);
int currentVersion = stat.getVersion();
// 2. 业务处理
System.out.println("当前数据: " + new String(data));
try {
// 3. 使用版本号更新(版本必须匹配)
zk.setData(path, newData.getBytes(), currentVersion);
System.out.println("更新成功");
} catch (KeeperException.BadVersionException e) {
// 4. 版本冲突,需要重试
System.out.println("版本冲突,数据已被其他客户端修改");
}
}
}
# 命令行中的版本控制
[zk: localhost:2181(CONNECTED) 0] create /version-test "v1"
Created /version-test
[zk: localhost:2181(CONNECTED) 1] stat /version-test | grep dataVersion
dataVersion = 0
[zk: localhost:2181(CONNECTED) 2] set /version-test "v2" 0
# 版本匹配,更新成功
[zk: localhost:2181(CONNECTED) 3] set /version-test "v3" 0
KeeperErrorCode = BadVersion for /version-test
# 版本已变为1,更新失败
版本控制保证了事务的原子性和隔离性,防止并发修改导致的数据覆盖。
五、事务的持久化存储
5.1 事务日志(Transaction Log)
所有事务操作都会先写入事务日志,然后才应用到内存数据库:
// 事务日志写入的关键特性
public class TransactionLog {
public void logTransaction(Txn txn) {
// 1. 预分配日志文件(1MB块,避免磁盘寻道)
// 2. 追加写入事务记录
// 3. 每条记录末尾写入签名,便于恢复时验证
// 4. 定期刷盘(forceSync配置)
}
}
事务日志保证了事务的持久性:即使服务器宕机,已提交的事务也不会丢失。
5.2 数据快照(Snapshot)
为避免启动时重放过多的日志,ZooKeeper会定期生成数据快照:
// 快照生成机制
public class DataTree {
public void takeSnapshot() {
// 1. 开始快照时记录当前ZXID
// 2. 异步序列化数据树
// 3. 快照文件名包含快照时的ZXID
// 4. 启动时加载最新快照,重放之后的事务日志
}
}
六、事务的一致性和隔离性
6.1 顺序一致性
ZooKeeper保证所有事务按ZXID严格顺序执行:
public class OrderGuarantee {
public void demonstrateOrder() {
ZooKeeper zk = new ZooKeeper("localhost:2181", 5000, null);
// 这些操作的ZXID严格递增
zk.create("/order1", "data1".getBytes(), ...); // ZXID=0x100000001
zk.create("/order2", "data2".getBytes(), ...); // ZXID=0x100000002
zk.create("/order3", "data3".getBytes(), ...); // ZXID=0x100000003
// 所有节点看到的数据变更顺序相同
}
}
6.2 单一系统映像
一旦事务提交,所有节点最终会看到相同的数据视图。但ZooKeeper不保证强一致性读——读操作可能返回稍旧的数据。
6.3 与数据库事务的区别
| ACID | 完全支持 | 部分支持(A、C、D,I较弱) |
| 隔离级别 | 可配置(读未提交到串行化) | 读取未提交(可能读到未提交数据) |
| 回滚 | 支持ROLLBACK | 不支持回滚,失败则整体放弃 |
| 多操作 | 任意多个SQL | 通过multi支持多个操作 |
七、总结
7.1 事务支持的核心机制
| ZXID全局有序 | 唯一标识事务,确定顺序 | 顺序一致性 |
| ZAB消息广播 | 原子广播到所有节点 | 原子性、一致性 |
| multi命令 | 多操作打包执行 | 多操作原子性 |
| 版本控制 | 乐观锁并发控制 | 隔离性、防止覆盖 |
| 事务日志 | 持久化记录事务 | 持久性 |
| 过半确认 | 多数派提交机制 | 可用性与一致性平衡 |
7.2 事务处理完整流程
#mermaid-svg-EpZGVN1RbiUrgoIp{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EpZGVN1RbiUrgoIp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EpZGVN1RbiUrgoIp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EpZGVN1RbiUrgoIp .error-icon{fill:#552222;}#mermaid-svg-EpZGVN1RbiUrgoIp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EpZGVN1RbiUrgoIp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EpZGVN1RbiUrgoIp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EpZGVN1RbiUrgoIp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EpZGVN1RbiUrgoIp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EpZGVN1RbiUrgoIp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EpZGVN1RbiUrgoIp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EpZGVN1RbiUrgoIp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EpZGVN1RbiUrgoIp .marker.cross{stroke:#333333;}#mermaid-svg-EpZGVN1RbiUrgoIp svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EpZGVN1RbiUrgoIp p{margin:0;}#mermaid-svg-EpZGVN1RbiUrgoIp .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-EpZGVN1RbiUrgoIp .cluster-label text{fill:#333;}#mermaid-svg-EpZGVN1RbiUrgoIp .cluster-label span{color:#333;}#mermaid-svg-EpZGVN1RbiUrgoIp .cluster-label span p{background-color:transparent;}#mermaid-svg-EpZGVN1RbiUrgoIp .label text,#mermaid-svg-EpZGVN1RbiUrgoIp span{fill:#333;color:#333;}#mermaid-svg-EpZGVN1RbiUrgoIp .node rect,#mermaid-svg-EpZGVN1RbiUrgoIp .node circle,#mermaid-svg-EpZGVN1RbiUrgoIp .node ellipse,#mermaid-svg-EpZGVN1RbiUrgoIp .node polygon,#mermaid-svg-EpZGVN1RbiUrgoIp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EpZGVN1RbiUrgoIp .rough-node .label text,#mermaid-svg-EpZGVN1RbiUrgoIp .node .label text,#mermaid-svg-EpZGVN1RbiUrgoIp .image-shape .label,#mermaid-svg-EpZGVN1RbiUrgoIp .icon-shape .label{text-anchor:middle;}#mermaid-svg-EpZGVN1RbiUrgoIp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EpZGVN1RbiUrgoIp .rough-node .label,#mermaid-svg-EpZGVN1RbiUrgoIp .node .label,#mermaid-svg-EpZGVN1RbiUrgoIp .image-shape .label,#mermaid-svg-EpZGVN1RbiUrgoIp .icon-shape .label{text-align:center;}#mermaid-svg-EpZGVN1RbiUrgoIp .node.clickable{cursor:pointer;}#mermaid-svg-EpZGVN1RbiUrgoIp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EpZGVN1RbiUrgoIp .arrowheadPath{fill:#333333;}#mermaid-svg-EpZGVN1RbiUrgoIp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EpZGVN1RbiUrgoIp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EpZGVN1RbiUrgoIp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EpZGVN1RbiUrgoIp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EpZGVN1RbiUrgoIp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EpZGVN1RbiUrgoIp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EpZGVN1RbiUrgoIp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EpZGVN1RbiUrgoIp .cluster text{fill:#333;}#mermaid-svg-EpZGVN1RbiUrgoIp .cluster span{color:#333;}#mermaid-svg-EpZGVN1RbiUrgoIp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EpZGVN1RbiUrgoIp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EpZGVN1RbiUrgoIp rect.text{fill:none;stroke-width:0;}#mermaid-svg-EpZGVN1RbiUrgoIp .icon-shape,#mermaid-svg-EpZGVN1RbiUrgoIp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EpZGVN1RbiUrgoIp .icon-shape p,#mermaid-svg-EpZGVN1RbiUrgoIp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EpZGVN1RbiUrgoIp .icon-shape rect,#mermaid-svg-EpZGVN1RbiUrgoIp .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EpZGVN1RbiUrgoIp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EpZGVN1RbiUrgoIp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EpZGVN1RbiUrgoIp :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
否
是
客户端发送写请求
是否Leader?
转发给Leader
预处理: 分配ZXID, 检查权限
广播提案给Follower
Follower写入事务日志
Follower返回ACK
收到过半ACK?
事务失败
广播COMMIT
应用内存数据库
响应客户端
7.3 一句话总结
ZooKeeper通过ZAB协议实现事务的原子广播,通过ZXID保证全局顺序,通过版本控制支持乐观锁并发,通过multi命令支持多操作原子性,最终构建了一个高效可靠的分布式事务处理系统,为分布式协调提供了坚实的基础。

|
🌺The End🌺点点关注,收藏不迷路🌺 |

