-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathseata-at-globallock.drawio
136 lines (136 loc) · 35.6 KB
/
seata-at-globallock.drawio
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<mxfile host="Electron" modified="2025-01-07T16:49:13.346Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.5 Chrome/114.0.5735.243 Electron/25.3.1 Safari/537.36" etag="tlbJu1--4azu7ervS9U-" version="21.6.5" type="device">
<diagram name="第 1 页" id="anDfxD5Yjz07njIsCnzu">
<mxGraphModel dx="2901" dy="764" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-1" value="<h1 style="font-size: 16px;"><font style="font-size: 16px;">Seata AT GlobalLock工作原理 (v2.0.0)</font></h1><div>Seata 全局锁用于保证不同分布式事务的<b>写隔离</b>和<b>读隔离;</b><br>这里只分析基于DB实现的全局锁实现原理。</div><p></p>" style="text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" vertex="1" parent="1">
<mxGeometry x="40" y="10" width="620" height="70" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-2" target="IiQ-Olb6a7n2lc8_pSmE-4">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-6" value="..." style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="IiQ-Olb6a7n2lc8_pSmE-5">
<mxGeometry x="-0.2" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-2" value="<b>全局锁的获取</b><br><font color="#007fff">发生在TC处理分支事务注册请求处理流程中</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="40" y="120" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-3" value="<b>全局锁的释放</b>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="40" y="1080" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-8" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=11;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-4" target="IiQ-Olb6a7n2lc8_pSmE-7">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-4" value="AbstractCore#<b style="font-size: 11px;">branchSessionLock</b>()" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="280" y="120" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-11" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=11;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-7" target="IiQ-Olb6a7n2lc8_pSmE-10">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-7" value="<div style="font-size: 11px;">protected void branchSessionLock(GlobalSession globalSession, BranchSession branchSession)</div><div style="font-size: 11px;">&nbsp; &nbsp; throws TransactionException {</div><div style="font-size: 11px;">&nbsp; &nbsp; String applicationData = branchSession.getApplicationData();</div><div style="font-size: 11px;">&nbsp; &nbsp; boolean autoCommit = true;</div><div style="font-size: 11px;">&nbsp; &nbsp; boolean skipCheckLock = false;</div><div style="font-size: 11px;"><font color="#007fff" style="font-size: 11px;">&nbsp; &nbsp; ... applicationData 不为空的话,从里面读取 autoCommit skipCheckLock 的值 ...</font></div><div style="font-size: 11px;">&nbsp; &nbsp; try {</div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; if (!branchSession.<b style="font-size: 11px;">lock</b>(autoCommit, skipCheckLock)) {</div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new BranchTransactionException(LockKeyConflict, ...</div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; branchSession.getBranchId()));</div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; }</div><div style="font-size: 11px;">&nbsp; &nbsp; } catch (StoreException e) {</div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; Throwable cause = e.getCause();</div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; if (cause instanceof BranchTransactionException) {</div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new BranchTransactionException(...);</div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; }</div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; throw e;</div><div style="font-size: 11px;">&nbsp; &nbsp; }</div><div style="font-size: 11px;">}</div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=2;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="520" y="120" width="440" height="280" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-9" value="ATCore" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="710" y="90" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-24" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-10" target="IiQ-Olb6a7n2lc8_pSmE-23">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-10" value="<div style="font-size: 11px;">public boolean lock(boolean autoCommit, boolean skipCheckLock) throws TransactionException {</div><div style="font-size: 11px;">&nbsp; &nbsp; if (this.branchType.equals(BranchType.<b style="font-size: 11px;">AT</b>)) {<font color="#007fff" style="font-size: 11px;"> // 即全局锁仅用于AT模式</font></div><div style="font-size: 11px;">&nbsp; &nbsp; &nbsp; &nbsp; return lockManager.<b style="font-size: 11px;">acquireLock</b>(this, autoCommit, skipCheckLock);</div><div style="font-size: 11px;">&nbsp; &nbsp; }</div><div style="font-size: 11px;">&nbsp; &nbsp; return true;</div><div style="font-size: 11px;">}</div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=4;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="1000" y="200" width="440" height="120" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-12" value="<font style="font-size: 12px;">BranchSession</font>" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="1170" y="170" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-13" value="<p style="margin:0px;margin-top:4px;text-align:center;"><b>LockManager</b><br></p><hr size="1"><p style="margin:0px;margin-left:4px;"></p><p style="margin:0px;margin-left:4px;">boolean <b>acquireLock</b>(BranchSession branchSession) throws TransactionException;<br></p><p style="margin:0px;margin-left:4px;">boolean <b>acquireLock</b>(BranchSession branchSession, boolean autoCommit, boolean skipCheckLock) throws TransactionException;<br></p><p style="margin:0px;margin-left:4px;">boolean <b>releaseLock</b>(BranchSession branchSession) throws TransactionException;<br></p><p style="margin:0px;margin-left:4px;">boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException;<br></p><p style="margin:0px;margin-left:4px;">...</p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="-440" y="120" width="400" height="200" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-19" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=block;endFill=1;dashed=1;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-18" target="IiQ-Olb6a7n2lc8_pSmE-13">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-18" value="<p style="margin:0px;margin-top:4px;text-align:center;"><b>AbstractLockManager</b><br></p><hr size="1"><p style="margin:0px;margin-left:4px;"><br></p><hr size="1"><p style="margin:0px;margin-left:4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;whiteSpace=wrap;" vertex="1" parent="1">
<mxGeometry x="-440" y="360" width="400" height="120" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-21" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endArrow=block;endFill=1;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-20" target="IiQ-Olb6a7n2lc8_pSmE-18">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-20" value="<p style="margin:0px;margin-top:4px;text-align:center;"><b>DataBaseLockManager</b><br></p><hr size="1"><p style="margin:0px;margin-left:4px;">private Locker locker;</p><hr size="1"><p style="margin:0px;margin-left:4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="-440" y="520" width="400" height="80" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-33" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-23" target="IiQ-Olb6a7n2lc8_pSmE-32">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-23" value="<div style="font-size: 11px;"><div>public boolean acquireLock(BranchSession branchSession, boolean autoCommit, <br>&nbsp; &nbsp; boolean skipCheckLock) throws TransactionException {</div><div>&nbsp; &nbsp; ...</div><div><font color="#007fff">&nbsp; &nbsp; <b>// lockKey 由“表名:主键值”组成,主键值可能有多个使用“,”连接,还可能有多个表:主键值,使用“;”连接</b></font></div><div>&nbsp; &nbsp; String lockKey = branchSession.<b>getLockKey</b>();</div><div>&nbsp; &nbsp; ...</div><div><font color="#007fff"><b>&nbsp; &nbsp; // 解析全部 "表名:主键值“ 结合分支会话信息组成一组行锁</b></font></div><div>&nbsp; &nbsp; List&lt;RowLock&gt; locks = <b>collectRowLocks</b>(branchSession);</div><div>&nbsp; &nbsp; if (CollectionUtils.isEmpty(locks)) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return true;</div><div>&nbsp; &nbsp; }</div><div><b><font color="#007fff">&nbsp; &nbsp; // DataBaseLockManager 初始化时创建了&nbsp;DataBaseLocker</font></b></div><div>&nbsp; &nbsp; return <b>getLocker</b>(branchSession).<b>acquireLock</b>(<b>locks</b>, autoCommit, skipCheckLock);</div><div>}</div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=4;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="1480" y="160" width="440" height="200" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-25" value="<p style="margin:0px;margin-top:4px;text-align:center;"><b>RowLock</b><br></p><hr size="1"><p style="margin:0px;margin-left:4px;"><b><font color="#007fff">行锁</font></b></p><p style="margin:0px;margin-left:4px;"><br></p><p style="margin:0px;margin-left:4px;">private String xid;</p><p style="margin:0px;margin-left:4px;">private Long transactionId;</p><p style="margin:0px;margin-left:4px;">//</p><p style="margin:0px;margin-left:4px;">private Long branchId;</p><p style="margin:0px;margin-left:4px;">//</p><p style="margin:0px;margin-left:4px;">private String resourceId;</p><p style="margin:0px;margin-left:4px;"><font color="#007fff">// 表名</font></p><p style="margin:0px;margin-left:4px;">private String tableName;</p><p style="margin:0px;margin-left:4px;"><font color="#007fff">// 主键值</font></p><p style="margin:0px;margin-left:4px;">private String pk;</p><p style="margin:0px;margin-left:4px;"><font color="#007fff">//&nbsp;</font></p><p style="margin:0px;margin-left:4px;">private String <b>rowKey</b>;</p><p style="margin:0px;margin-left:4px;">private String feature;</p><hr size="1"><p style="margin:0px;margin-left:4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;whiteSpace=wrap;" vertex="1" parent="1">
<mxGeometry x="-1320" y="320" width="400" height="280" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-28" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;endArrow=block;endFill=1;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-26" target="IiQ-Olb6a7n2lc8_pSmE-27">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-26" value="<p style="margin:0px;margin-top:4px;text-align:center;"><b>AbstractLocker</b><br></p><hr size="1"><p style="margin:0px;margin-left:4px;"><br></p><p style="margin:0px;margin-left:4px;">protected static final String LOCK_SPLIT = "^^^";<br></p><hr size="1"><p style="margin:0px;margin-left:4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;whiteSpace=wrap;" vertex="1" parent="1">
<mxGeometry x="-880" y="320" width="400" height="120" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-27" value="<p style="margin:0px;margin-top:4px;text-align:center;"><b>Locker</b><br></p><hr size="1"><p style="margin:0px;margin-left:4px;"></p><p style="margin:0px;margin-left:4px;">boolean acquireLock(List&lt;RowLock&gt; rowLock) ;<br></p><p style="margin:0px;margin-left:4px;">boolean acquireLock(List&lt;RowLock&gt; rowLock, boolean autoCommit, boolean skipCheckLock);<br></p><p style="margin:0px;margin-left:4px;">boolean releaseLock(List&lt;RowLock&gt; rowLock);<br></p><p style="margin:0px;margin-left:4px;">boolean releaseLock(String xid, Long branchId);<br></p><p style="margin:0px;margin-left:4px;">boolean releaseLock(String xid);<br></p><p style="margin:0px;margin-left:4px;">...</p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="-880" y="120" width="400" height="160" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-30" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endArrow=block;endFill=1;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-29" target="IiQ-Olb6a7n2lc8_pSmE-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-29" value="<p style="margin:0px;margin-top:4px;text-align:center;"><b>DataBaseLocker</b><br></p><hr size="1"><p style="margin:0px;margin-left:4px;"></p><p style="margin:0px;margin-left:4px;"><br></p><p style="margin:0px;margin-left:4px;"><font color="#007fff"><b>// DAO 接口</b></font></p><p style="margin:0px;margin-left:4px;">private LockStore <b>lockStore</b>;<br></p><hr size="1"><p style="margin:0px;margin-left:4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="-880" y="480" width="400" height="120" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-31" value="<font color="#007fff">锁和锁管理器也有四种实现,这里只分析基于关系型数据库的实现</font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="-440" y="620" width="370" height="30" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-36" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-32" target="IiQ-Olb6a7n2lc8_pSmE-35">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="2420" y="260" />
<mxPoint x="2420" y="420" />
<mxPoint x="260" y="420" />
<mxPoint x="260" y="740" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-43" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-32" target="IiQ-Olb6a7n2lc8_pSmE-42">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-32" value="<div style=""><font color="#007fff">//LockDO 是DB数据表中的数据对象</font></div><div style="">return lockStore.<b>acquireLock</b>(<b>convertToLockDO</b>(locks), autoCommit, skipCheckLock);<br></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=10;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="1960" y="230" width="440" height="60" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-34" value="LockStoreDataBaseDAO" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="2100" y="200" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-38" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="IiQ-Olb6a7n2lc8_pSmE-35" target="IiQ-Olb6a7n2lc8_pSmE-37">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-35" value="<div style=""><div>public boolean acquireLock(List&lt;LockDO&gt; lockDOs, boolean autoCommit, boolean skipCheckLock) {</div><div>&nbsp; &nbsp; ...</div><div><font color="#007fff">&nbsp; &nbsp; // LockDO 去重</font></div><div>&nbsp; &nbsp; if (lockDOs.size() &gt; 1) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; lockDOs = lockDOs.stream().filter(LambdaUtils.<b>distinctByKey</b>(LockDO::getRowKey)).collect(Collectors.toList());</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; try {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; conn = lockStoreDataSource.getConnection();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (originalAutoCommit = conn.getAutoCommit()) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; conn.setAutoCommit(false);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; List&lt;LockDO&gt; unrepeatedLockDOs = lockDOs;</div><div><br></div><div><font color="#007fff"><b>&nbsp; &nbsp; &nbsp; &nbsp; //获取锁前校验,需要先校验这些锁是否已经存在,有任意一个锁存在都不能获取锁</b></font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (!skipCheckLock) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; boolean canLock = true;</div><div><font color="#007fff">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 从锁的数据表中查询这些锁是否已经存在</font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String checkLockSQL = LockStoreSqlFactory.getLogStoreSql(dbType).<b>getCheckLockableSql</b>(lockTable, lockDOs.size());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div><br></div><div><font color="#007fff"><b>&nbsp; &nbsp; &nbsp; &nbsp; // lockDOs中的锁都不存在,则获取这些锁,就是将它们插入到数据表</b></font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (unrepeatedLockDOs.size() == 1) {&nbsp; <font color="#007fff">// 只有一个锁对象</font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LockDO lockDO = unrepeatedLockDOs.get(0);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!doAcquireLock(conn, lockDO)) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; conn.<b>rollback</b>(); <font color="#007fff">// 可能插入前其他事务将锁插入了数据表,则当前事务获取锁失败,还需要当前事务将已经插入的锁回滚</font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; } else {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!doAcquireLocks(conn, unrepeatedLockDOs)) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; conn.rollback();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; conn.commit();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return true;</div><div>&nbsp; &nbsp; } catch (SQLException e) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; throw new StoreException(e);</div><div>&nbsp; &nbsp; } finally {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ... 释放连接,恢复自动提交 ...</div><div>&nbsp; &nbsp; }</div><div>}</div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=2;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="280" y="440" width="680" height="600" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-37" value="<div style=""><div>protected boolean doAcquireLock(Connection conn, LockDO lockDO) {</div><div>&nbsp; &nbsp; PreparedStatement ps = null;</div><div>&nbsp; &nbsp; try {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; String insertLockSQL = LockStoreSqlFactory.<b>getLogStoreSql</b>(dbType)<br><span style="white-space: pre;">	</span>&nbsp; &nbsp; .getInsertLockSQL(lockTable);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ps = conn.prepareStatement(insertLockSQL);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ps.setString(1, lockDO.getXid());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ps.setLong(2, lockDO.getTransactionId());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ps.setLong(3, lockDO.getBranchId());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ps.setString(4, lockDO.getResourceId());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ps.setString(5, lockDO.getTableName());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ps.setString(6, lockDO.getPk());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ps.setString(7, lockDO.<b>getRowKey</b>());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ps.setInt(8, LockStatus.Locked.getCode());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return ps.executeUpdate() &gt; 0;</div><div>&nbsp; &nbsp; } catch (SQLException e) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (e instanceof SQLIntegrityConstraintViolationException) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; throw new StoreException(e);</div><div>&nbsp; &nbsp; } finally {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; IOUtil.close(ps);</div><div>&nbsp; &nbsp; }</div><div>}</div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=2;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="1000" y="580" width="440" height="320" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-39" value="<font color="#007fff"><b>insert into&nbsp; #lock_table# (xid, transaction_id, branch_id, resource_id, table_name, pk, row_key, gmt_create, gmt_modified,status) </b><br><b>values (?, ?, ?, ?, ?, ?, ?, now(), now(), ?);&nbsp;<br></b><br><b>获取锁原理就是基于主键唯一索引(row_key)进行插入,插入成功获取锁成功,插入失败获取锁失败,和Redis set nx 原理一样。</b><br></font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="1450" y="565" width="760" height="70" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-40" value="<div><font color="#007fff">CREATE TABLE IF NOT EXISTS `<b>lock_table</b>`</font></div><div><font color="#007fff">(</font></div><div><font color="#007fff">&nbsp; &nbsp; `row_key`&nbsp; &nbsp; &nbsp; &nbsp; VARCHAR(128) NOT NULL,</font></div><div><font color="#007fff">&nbsp; &nbsp; `xid`&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; VARCHAR(128),</font></div><div><font color="#007fff">&nbsp; &nbsp; `transaction_id` BIGINT,</font></div><div><font color="#007fff">&nbsp; &nbsp; `branch_id`&nbsp; &nbsp; &nbsp; BIGINT&nbsp; &nbsp; &nbsp; &nbsp;NOT NULL,</font></div><div><font color="#007fff">&nbsp; &nbsp; `resource_id`&nbsp; &nbsp; VARCHAR(256),</font></div><div><font color="#007fff">&nbsp; &nbsp; `table_name`&nbsp; &nbsp; &nbsp;VARCHAR(32),</font></div><div><font color="#007fff">&nbsp; &nbsp; `pk`&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;VARCHAR(36),</font></div><div><font color="#007fff">&nbsp; &nbsp; `status`&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TINYINT&nbsp; &nbsp; &nbsp; NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',</font></div><div><font color="#007fff">&nbsp; &nbsp; `gmt_create`&nbsp; &nbsp; &nbsp;DATETIME,</font></div><div><font color="#007fff">&nbsp; &nbsp; `gmt_modified`&nbsp; &nbsp;DATETIME,</font></div><div><font color="#007fff">&nbsp; &nbsp; <b>PRIMARY KEY</b> (`<b>row_key</b>`),</font></div><div><font color="#007fff">&nbsp; &nbsp; KEY `idx_status` (`status`),</font></div><div><font color="#007fff">&nbsp; &nbsp; KEY `idx_branch_id` (`branch_id`),</font></div><div><font color="#007fff">&nbsp; &nbsp; KEY `idx_xid` (`xid`)</font></div><div><font color="#007fff">) ENGINE = InnoDB</font></div><div><font color="#007fff">&nbsp; DEFAULT CHARSET = utf8mb4;</font></div>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="1450" y="640" width="500" height="270" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-41" value="<font color="#007fff">即删除锁的行记录</font>" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="255" y="1095" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-42" value="<div style=""><div>protected LockDO convertToLockDO(RowLock rowLock) {</div><div>&nbsp; &nbsp; LockDO lockDO = new LockDO();</div><div>&nbsp; &nbsp; lockDO.setBranchId(rowLock.getBranchId());</div><div>&nbsp; &nbsp; lockDO.setPk(rowLock.getPk());</div><div>&nbsp; &nbsp; lockDO.setResourceId(rowLock.getResourceId());</div><div><font style="" color="#007fff"><b>&nbsp; &nbsp; // 锁记录的主键是 resourceId^^^tableName^^^pk 拼接成的,比如&nbsp;</b><br><b>&nbsp; &nbsp; //&nbsp;</b></font><font color="#007fff"><b>jdbc:mysql://127.0.0.1:13306/seata_product^^^product^^^1</b></font></div><div>&nbsp; &nbsp; lockDO.<b>setRowKey</b>(<b>getRowKey</b>(rowLock.getResourceId(), rowLock.getTableName(), rowLock.getPk()));</div><div>&nbsp; &nbsp; lockDO.setXid(rowLock.getXid());</div><div>&nbsp; &nbsp; lockDO.setTransactionId(rowLock.getTransactionId());</div><div>&nbsp; &nbsp; lockDO.setTableName(rowLock.getTableName());</div><div>&nbsp; &nbsp; return lockDO;</div><div>}</div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=3;fontSize=11;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="1">
<mxGeometry x="2440" y="160" width="440" height="200" as="geometry" />
</mxCell>
<mxCell id="IiQ-Olb6a7n2lc8_pSmE-44" value="<b>获取锁原理就是基于主键唯一索引(row_key)进行插入,<br>插入成功获取锁成功,插入失败获取锁失败,和Redis set nx 原理一样。</b>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="40" y="200" width="410" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>