死锁问题的由来
关系型数据库在并行处理写事务的时候需要解决写写冲突,也就是我们常说的并发控制问题。主流数据库一般采用锁机制来保证写操作处理的并发性和数据的一致性。引入锁机制会不可避免的带来锁等待(或者叫锁冲突)问题。有一类特殊的锁等待被称为死锁:两个及以上事务如果持有各自的锁资源,同时又等待对方事务的锁资源。和锁等待不同的是,死锁是一种资源的循环等待,如果不借助外部力量是没有办法被消除的。死锁会导致数据库并发控制能力的下降,因此主流商业数据库都有一套检测和解决死锁的机制。
相对来说,集中式数据库由于资源集中,视图统一,较容易发现并处理死锁。而分布式数据则在死锁检测上存在天然的困难,如果处理的不好,死锁检测就会变成一种集中式的处理,失去分布式数据库的优势和效率。OceanBase 从 4.x 开始引入了一个分布式死锁检测机制,名叫“Lock Chain Length-based Distributed Algorithm”,以下简称 LCL。它可以在不需要全局或本地锁资源视图的情况下,仅通过每一个等待事务给被等待事务发送一些信息(几十字节),就能检测死锁,实现了真正意义上的分布式死锁检测。
常见的死锁检测算法
- 集中式死锁检测
由一个控制机维护一个全局的有向图(Wait-for Graph,简称 WFG),通过在有向图中发现是否存在环的方式来检测死锁。 - 分布式死锁检测
分布式死锁算法将死锁检测的责任委托给各个节点,通过节点间的消息传递来检测死锁。- Path-Pushing algorithm
- Edge-Chasing algorithm
- Diffusion algorithm
- Global State algorithm
- 层次式死锁检测
每个机器都有本地检测器,它们相互通信本地的 WFG,并使用本地 WFG 检测单机内的死锁。每个机器还将其本地 WFG 发送给下一级机器的死锁检测器,分布式死锁将由这些机器的死锁检测器来检测。
分布式死锁检测算法的优劣
以最常见的 Path-Pushing 算法为例,它的原理大致是这样的:
在给定机器 S 中,算法使用不存在的虚拟机器 EXTERNAL(“EX”)来表示 S 以外的机器。如果事务 T1 等待事务 T2,TWFG 中就会创建一条 T1 → T2 的边。当一个事务等待外部机器的某些事务时,用 T1 → EX 边表示。类似地,当一个外部机器中的事务等待自己时,用 EX → T2 表示。
多个机器之间会交换同步这些信息。Path-Pushing 算法在多出度场景下大多存在多杀以及误杀的问题。
在 Edge-Chasing 算法和核心思想中,会在事务节点间传递一个令牌,如果令牌经由自己发出最后又回到自己,就说明发现了一个死锁环。但是在一个事务可能阻塞多个事务(多出度)的场景下,有可能出现死锁环中传递令牌并不属于这个环中任何一个事务的情况,这样就检测不到死锁,称为“环外污染”。
LCL 死锁检测算法引入了“路径深度”(Lock Chain Length Value)的概念。处于死锁环中事务的 LCLV 会随时间推移无限增长,而不在死锁环中,并且也不在任何死锁环下游的事务的 LCLV 则存在增长上限。LCL 使令牌只在较大的 LCLV 事务间进行传递来避免“环外污染”,从而发现死锁。
死锁实例
配置参数
参数 | 默认值 | 范围 | 生效方式 | 含义 |
---|---|---|---|---|
_lcl_op_interval | 30ms | 集群 | 动态 | 动态 LCL死锁检测算法中节点定期消息推送间隔,该值越小,探测死锁的延迟就越低,但是对系统资源的消耗将会变大,当该值设置为0时,表示关闭死锁检测功能。 |
构造死锁
仅需要两个事物互相等待对方持有的锁就能构成死锁,为了更好理解,我们构造结构稍微复杂一点的,图中有两个死锁环:
为了不让其他超时时间干扰实验,需要将 ob_query_timeout,ob_trx_idle_timeout,ob_trx_lock_timeout,ob_trx_timeout 设的足够大,并关闭 autocommit。
建表插入数据:
[test]> create table tdl(id int primary key, value int);
[test]> select * from tdl;
+----+-------+
| id | value |
+----+-------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
| 4 | 400 |
| 5 | 500 |
| 6 | 600 |
| 7 | 700 |
| 8 | 800 |
+----+-------+
8 rows in set (0.002 sec)
开启 8 个终端,分别执行:
update tdl set value=111 where id=1;
update tdl set value=222 where id=2;
update tdl set value=333 where id=3;
update tdl set value=444 where id=4;
update tdl set value=555 where id=5;
update tdl set value=666 where id=6;
update tdl set value=777 where id=7;
update tdl set value=888 where id=8;
再在 8 个终端分别执行,就可以构造上图中的死锁结构:
update tdl set value=111 where id=2;
update tdl set value=222 where id=3;
update tdl set value=333 where id=1;
update tdl set value=444 where id=3;
update tdl set value=555 where id in (4,6);
update tdl set value=666 where id=7;
update tdl set value=777 where id=5;
update tdl set value=888 where id=7;
观察结果
按时间线编号:
可以看到:
两个死锁分别在 2024-03-24 14:17:55.525352 和 2024-03-24 14:18:05.774349 被检测到,且优先级最低的事务被 killed,我们在 __all_virtual_deadlock_event_history 也能找到这两个事件的六条记录:
[oceanbase]> select * from __all_virtual_deadlock_event_history order by report_time\G
*************************** 17. row ***************************
tenant_id: 1002
event_id: 17363499343403574499
svr_ip: 10.137.71.17
svr_port: 42122
detector_id: 638
report_time: 2024-03-24 14:17:55.525352
cycle_idx: 3
cycle_size: 3
role: witness
priority_level: normal
priority: 18445032812732308224
create_time: 2024-03-24 14:17:50.360447
start_delay: 0
module: transaction
visitor: {"INT":3}
object: {"INT":3}
extra_name1: current sql
extra_value1: update tdl set value=222 where id=3
extra_name2:
extra_value2:
extra_name3:
extra_value3:
*************************** 18. row ***************************
tenant_id: 1002
event_id: 17363499343403574499
svr_ip: 10.137.71.17
svr_port: 42122
detector_id: 643
report_time: 2024-03-24 14:17:55.525352
cycle_idx: 1
cycle_size: 3
role: victim
priority_level: normal
priority: 18445032812727073061
create_time: 2024-03-24 14:17:54.418178
start_delay: 0
module: transaction
visitor: {"INT":1}
object: {"INT":1}
extra_name1: current sql
extra_value1: update tdl set value=333 where id=1
extra_name2:
extra_value2:
extra_name3:
extra_value3:
*************************** 19. row ***************************
tenant_id: 1002
event_id: 17363499343403574499
svr_ip: 10.137.71.17
svr_port: 42122
detector_id: 645
report_time: 2024-03-24 14:17:55.525352
cycle_idx: 2
cycle_size: 3
role: witness
priority_level: normal
priority: 18445032812741186164
create_time: 2024-03-24 14:17:55.445270
start_delay: 0
module: transaction
visitor: {"INT":2}
object: {"INT":2}
extra_name1: current sql
extra_value1: update tdl set value=111 where id=2
extra_name2:
extra_value2:
extra_name3:
extra_value3:
*************************** 20. row ***************************
tenant_id: 1002
event_id: 12049350011482659381
svr_ip: 10.137.71.17
svr_port: 42122
detector_id: 648
report_time: 2024-03-24 14:18:05.774349
cycle_idx: 3
cycle_size: 3
role: witness
priority_level: normal
priority: 18445032812709175775
create_time: 2024-03-24 14:18:04.420681
start_delay: 0
module: transaction
visitor: {"INT":7}
object: {"INT":7}
extra_name1: current sql
extra_value1: update tdl set value=666 where id=7
extra_name2:
extra_value2:
extra_name3:
extra_value3:
*************************** 21. row ***************************
tenant_id: 1002
event_id: 12049350011482659381
svr_ip: 10.137.71.17
svr_port: 42122
detector_id: 649
report_time: 2024-03-24 14:18:05.774349
cycle_idx: 1
cycle_size: 3
role: victim
priority_level: normal
priority: 18445032812704316133
create_time: 2024-03-24 14:18:04.420682
start_delay: 0
module: transaction
visitor: {"INT":5}
object: {"INT":5}
extra_name1: current sql
extra_value1: update tdl set value=777 where id=5
extra_name2:
extra_value2:
extra_name3:
extra_value3:
*************************** 22. row ***************************
tenant_id: 1002
event_id: 12049350011482659381
svr_ip: 10.137.71.17
svr_port: 42122
detector_id: 653
report_time: 2024-03-24 14:18:05.774349
cycle_idx: 2
cycle_size: 3
role: witness
priority_level: normal
priority: 18445032812714570559
create_time: 2024-03-24 14:18:05.695048
start_delay: 0
module: transaction
visitor: {"INT":6}
object: {"INT":6}
extra_name1: current sql
extra_value1: update tdl set value=555 where id in (4,6)
extra_name2:
extra_value2:
extra_name3:
extra_value3:
22 rows in set (0.01 sec)
算法介绍
LCL 算法原文参见参考,本文是对原文的一个科普,为了方便理解,做了一定程度的简化。
LCL 算法的基本思想是:为每个事务定义一个 Lock Chain Length Value(LCLV),事务的初始 LCLV 为 0,如果存在事务 A 等待事务 B,则让 B.LCLV = A.LCLV + 1,也就是下游事务的 LCLV 值要比上游事务的 LCLV 大 1,并且不停地传播下去。可以想象,如果是一个锁等待链,经过一定时间的传递,各个事务的 LCLV 就会达到一个上限不再变化,并且下游事务的 LCLV 总是大于上游事务对的 LCLV;如果是一个死锁环,经过一定时间的传递,各个事务的 LCLV 则会变得非常大,没有上限。这时,LCLV 很大的事务要么是一个死锁环上的事务,要么是某个死锁环的下游事务。这个阶段被称为繁殖期。
第二阶段,让等待链上的事务拥有向下游传播自己令牌的机会,并且高优先级令牌会覆盖低优先级令牌,这个阶段被称为传播期。可以想象,在死锁环中,这样的令牌最终一定会被传递自己手上,一旦发生了这种情况,就表明死锁被发现了。
第三阶段,只需要 kill 这个事务,死锁环就会被打破。
这个算法的精妙之处在于,如果系统中存在死锁,则死锁一定会被发现(存在性),被 killed 的事务只有一个,不会多杀(唯一性)。如果系统中不存在死锁,也不会错杀。
定义
每个等待或者被等待的事务被称为是一个节点,
A
→
B
A\to B
A→B表示
A
A
A等待
B
B
B的锁,
A
A
A到
B
B
B的步长记为 1。
A
→
B
→
C
A\to B\to C
A→B→C表示
A
A
A等待
B
B
B的锁,
B
B
B等待
C
C
C的锁,并可表示成
A
⇒
C
A\Rightarrow C
A⇒C。
A
A
A到
C
C
C的步长记为 2,以此类推。
A
A
A和
B
B
B都有自己的私有令牌和公共令牌,记为
A
.
P
r
A
P
,
A
.
P
u
A
P
,
B
.
P
r
A
P
,
B
.
P
u
A
P
A.PrAP,A.PuAP,B.PrAP,B.PuAP
A.PrAP,A.PuAP,B.PrAP,B.PuAP
如果存在一个节点到
A
A
A的步长为
m
m
m,则记
A
.
L
C
L
V
=
m
A.LCLV=m
A.LCLV=m,每个节点的初始
L
C
L
V
=
0
LCLV=0
LCLV=0。
繁殖期
对于每一个 A A A等待 B B B的锁,也就是 A → B A\to B A→B的关系,执行:
LOOP
A.PuAP = A.PrAP
B.PuAP = B.PrAP
B.LCLV = max(B.LCLV, A.LCLV + 1)
这么做的目的,是为了让直接下游节点的 LCLV 比直接上游节点的 LCLV 大 1,经过一定次数的循环,则死锁环内节点的 LCLV 到达一个非常大的值,非死锁换上的节点,比如仅仅是锁等待链的节点的 LCLV 则收敛在某一个值,具体需要循环多少次才能稳定,可以参考下面的数学模型及论文原文。
再次注意:这时候 LCLV 很大的节点还有一种可能是位于死锁环下游的某个节点,我们不希望它有传播自己私有令牌的机会,具体看传播期。
一个繁殖期的时间周期是固定的,为 700ms。
注意:一轮繁殖是指图中每条边都会应用一次,但每条边应用的时间和次序是任意的。所以上图只是一种情况,实际的繁殖会有更多种变化情况。但无论如何变化都会结果不应有实质性的影响。从图中这个例子也能看出,顶端死锁环的真上游节点走到死锁环的最远距离就是保证这一阶段成立的最小轮次。
传播期
对于每一个 A A A等待 B B B的锁,也就是 A → B A\to B A→B的关系,执行:
LOOP
B.LCLV = max(B.LCLV, A.LCLV)
if B.LCLV == A.LCLV then
B.PuAP = max(B.PuAP, A.PuAP)
第一条指令会使得死锁环内节点的 LCLV 变得相同,且下游节点的 LCLV 不小于上游节点的 LCLV。
第二、三条指令则是让上游节点向 LCLV 不大于自己的下游节点传播自己的私有令牌。也就是说如果 LCLV 太小的上游节点是没有传播自己令牌的权利的,刚才我们讲过,死锁环内节点的 LCLV 都很大,因此死锁环内节点不会没有这个权利。但是我们刚才还分析过,还存在一种位于死锁环下游的某个节点(记为节点 P),P 的 LCLV 也很大,万一 P 传播了自己的令牌怎么办呢?仔细看其实是不会的,因为 P 的上游有一个死锁环,P 的 LCLV 之所以大,也是从 P 的上游死锁环繁殖下来了,即使 P 要传播令牌,也传播的是 P 上游的死锁环内某个节点的私有令牌,不是 P 自己的。有人可能还有疑问,这里不是还要比较令牌的较大值吗?万一 P 的令牌较大,阻碍了 P 继承上游死锁环内节点的令牌怎么办呢?这一点也不用担心,因为即使 P 向下游传播了 P 的令牌,这个令牌最终是回不到 P 自己手里的,也就不会被侦测到(参考侦测期)。
也就是说,处于最顶端的死锁环内的某个优先级最高的节点的令牌会被传播,并最终回到自己手里。注意:
- 因为这是一个最顶端的死锁环,因此即使它还有上游节点,那个上游节点的 LCLV 也会较小,回忆下刚才说的 LCLV 较大的节点只可能是死锁环内或者某个死锁环下游的节点
- 这里说的最顶端的死锁环(论文原文称为 topmost),指的是不存在在它上游的死锁环,所以并不唯一,可以理解成极大上游死锁环,而不是最大上游死锁环。也就是上下游的关系是一个偏序关系。
同样地,传播期也是需要循环一定次数才会稳定的,具体多少次数一定可以稳定呢,参考下面的数学模型及论文原文。
一个传播期的时间周期是固定的,为 700ms。
注意:一轮传播是指图中每条边都会应用一次,但每条边应用的时间和次序是任意的。所以上图只是一种情况,实际的传播会有更多种变化情况。但无论如何变化都会结果不应有实质性的影响。从图中这个例子也能看出,最多循环两次死锁环,就能保证结果成立。其中第一次循环是传播 max LCLV,第二次循环是传播 max PuAP,这是最坏情况,也就是 max LCLV 节点和 max PuAP 节点不是同一个,并且正好要走完一圈才能传播到,实际情况可能用不了那么多次。
侦测期
对于每一个 A A A等待 B B B的锁,也就是 A → B A\to B A→B的关系,执行:
if B.LCLV == A.LCLV and
B.PuAP == A.PuAP and
B.PuAP == B.PrAP then
kill B
按照上述解释,第一条指令和传播期的 if 是一样的,第二条指令则保证 B 收到的令牌是经由传播的,而不是自己一开始就赋予的,第三条指令表示经由传播收到的令牌和自己的私有令牌相同。这样的 B 就被选为 victim。
基于这样的算法,可以到达这样的效果:
- 如果多个死锁环相互之间没有关联,则可以在一轮算法检测中全部发现
- 如果一个强连通分量(定义见下文)中包含多个死锁环,则可以在一轮算法检测中发现一个死锁环中的节点(图 a)
- 如果多个死锁环呈链式分布,则在一轮算法检测中,可以保证发现最顶端的死锁环(定义见下文)中的节点(如果这个环内节点优先级最大),也可能发现多个死锁环中的节点(如果下游死锁环的优先级更大)(图 b)
数学模型
严格的数学定义和证明请参见论文原文,这里仅给出能够说明问题所需要最核心的部分,以及对证明思路的解读。
前文说的死锁环是一种通俗的讲法,模型中被抽象成是一种强连通分量(strongly connected component,简称 SCC),两个 SCC 或者交集为空,或者相同,任何一个与 SCC 有互相等待关系的节点也属于这个 SCC,SCC 的这种“吸收性”正好可以用来表达一个完整的死锁环。
定义
- A → B A\to B A→B表示 A A A等待 B B B的锁, A A A被称为是 B B B的直接上游, B B B被称为是 A A A的直接下游
- A ⇒ B A\Rightarrow B A⇒B表示存在从 A A A到 B B B的等待路径, A A A被称为是 B B B的上游, B B B被称为是 A A A的下游,既有 A ⇒ B A\Rightarrow B A⇒B又有 B ⇒ A B\Rightarrow A B⇒A,则记为 A ⇔ B A\Leftrightarrow B A⇔B
- < x , y > <x,y> <x,y>表示一个整数, x x x在高位, y y y在低位
- D i s t ( A , B ) Dist(A,B) Dist(A,B)表示 A A A到 B B B的最短步长
-
S
C
C
(
A
)
SCC(A)
SCC(A)表示一个包含
A
A
A节点的强连通分量,有如下性质:
- 如果 s c c 1 ∩ s c c 2 ≠ ∅ scc_1 \cap scc_2 \ne \emptyset scc1∩scc2=∅,则 s c c 1 = = s c c 2 scc_1 == scc_2 scc1==scc2
- 如果 X ∈ s c c 1 , Y ∈ s c c 2 , X ⇔ Y X\in scc_1,Y\in scc_2,X\Leftrightarrow Y X∈scc1,Y∈scc2,X⇔Y,则 s c c 1 = = s c c 2 scc_1 == scc_2 scc1==scc2
- 如果 X ⇔ Y X\Leftrightarrow Y X⇔Y,则 S C C ( X ) = = S C C ( Y ) SCC(X) == SCC(Y) SCC(X)==SCC(Y)
- 如果 X ∈ s c c 1 , Y ∈ s c c 1 X\in scc_1,Y\in scc_1 X∈scc1,Y∈scc1,则任意 X X X到 Y Y Y的路径也属于 s c c 1 scc_1 scc1
- 如果 X X X不在任何环内,则 S C C ( X ) = = { X } SCC(X)==\{ X\} SCC(X)=={X}(平凡的)
-
U
S
G
(
s
c
c
)
USG(scc)
USG(scc)表示某个
s
c
c
scc
scc的全部上游节点,也包含
s
c
c
scc
scc自身(平凡的),即:
U S G ( s c c ) = ( V S , E S ) , V S = { X : X ⇒ A , A ∈ s c c } , E S = { X → Y : X , Y ∈ V S } USG(scc)=(VS,ES),VS=\{X:X\Rightarrow A,A\in scc\},ES=\{X\to Y:X,Y\in VS\} USG(scc)=(VS,ES),VS={X:X⇒A,A∈scc},ES={X→Y:X,Y∈VS} -
A
S
G
(
s
c
c
)
ASG(scc)
ASG(scc)表示某个
s
c
c
scc
scc的全部真上游节点,也就是不包含
s
c
c
scc
scc自身,即:
A S G ( s c c ) = ( V S , E S ) , V S = { X : X ∈ U S G ( s c c ) , X ∉ s c c } , E S = { X → Y : X , Y ∈ V S } ASG(scc)=(VS,ES),VS=\{X:X\in USG(scc),X\notin scc\},ES=\{X\to Y:X,Y\in VS\} ASG(scc)=(VS,ES),VS={X:X∈USG(scc),X∈/scc},ES={X→Y:X,Y∈VS}
-
A
s
g
D
i
s
t
(
A
,
s
c
c
)
AsgDist(A,scc)
AsgDist(A,scc)表示
A
S
G
(
s
c
c
)
ASG(scc)
ASG(scc)中某个节点
A
A
A,走到
s
c
c
scc
scc中第一个节点的所有路径中,最大的步长,路径中不能有重复节点,即:
对于 A ∈ A S G ( s c c ) A\in ASG(scc) A∈ASG(scc)
A s g D i s t ( A , s c c ) : = m a x { m : ∃ C m ( = A ) → C m − 1 → . . . → C 1 → C 0 ( ∈ s c c ) } AsgDist(A,scc):=max\{m:\exists C_m(=A)\to C_{m-1}\to ...\to C_1\to C_0(\in scc)\} AsgDist(A,scc):=max{m:∃Cm(=A)→Cm−1→...→C1→C0(∈scc)},其中 C m , C m − 1 , . . . , C 1 , C 0 C_m,C_{m-1},...,C_1,C_0 Cm,Cm−1,...,C1,C0各不相同 -
A
s
g
W
i
d
t
h
(
s
c
c
)
AsgWidth(scc)
AsgWidth(scc)表示所有
A
S
G
(
s
c
c
)
ASG(scc)
ASG(scc)中节点的
A
s
g
D
i
s
t
(
A
,
s
c
c
)
AsgDist(A,scc)
AsgDist(A,scc)最大的那一个距离。可以理解为
A
S
G
(
s
c
c
)
ASG(scc)
ASG(scc)的宽度,其意义是
s
c
c
scc
scc的全部真上游节点中,最远那个走到
s
c
c
scc
scc所需要的步长。定义是:
A s g W i d t h ( s c c ) : = m a x { A s g D i s t ( X , s c c ) : X ∈ U S G ( s c c ) } AsgWidth(scc):=max\{AsgDist(X,scc):X\in USG(scc)\} AsgWidth(scc):=max{AsgDist(X,scc):X∈USG(scc)} -
S
c
c
D
i
a
m
(
s
c
c
)
SccDiam(scc)
SccDiam(scc)表示
s
c
c
scc
scc中所有节点,最远的两个节点的距离,可以理解为
s
c
c
scc
scc的直径,即:
S c c D i a m ( s c c ) : = m a x { D i s t ( X , Y ) : X , Y ∈ s c c , X ≠ Y } SccDiam(scc):=max\{Dist(X,Y):X,Y\in scc,X\ne Y\} SccDiam(scc):=max{Dist(X,Y):X,Y∈scc,X=Y}
- P r I D PrID PrID表示某个节点的私有 ID,整型数字,每个节点唯一
- P u I D PuID PuID表示某个节点的公共 ID,整型数字,初始等于 P r I D PrID PrID
- P r A P PrAP PrAP表示某个节点的私有优先级,整型数字
- P u A P PuAP PuAP表示某个节点的公共优先级,整型数字,初始等于 P r A P PrAP PrAP
- L C L V LCLV LCLV表示某个节点的 Lock Chain Length Value,如果 A . L C L V = m A.LCLV=m A.LCLV=m意味着存在一个到达节点 A 的路径,其路径长度为 m。
- 真上游 SCC, s c c 2 scc_2 scc2被称为是 s c c 1 scc_1 scc1的真上游 SCC,当 s c c 2 ⊆ A S G ( s c c 1 ) scc_2\subseteq ASG(scc_1) scc2⊆ASG(scc1)
- 顶端 SCC,一个 SCC 被称为是顶端 SCC,当它没有真上游 SCC。
有了这些定义,论文给出的结论是:
如果 s c c scc scc是一个顶端 s c c scc scc,那么经过 m a x ( 1 , A s g W i d t h ( s c c ) ) max(1,AsgWidth(scc)) max(1,AsgWidth(scc))个轮次的繁殖期,和 S c c D i a m ( s c c ) × 2 SccDiam(scc)\times2 SccDiam(scc)×2个轮次的传播期后,存在唯一的一个节点,这个节点是死锁环上的一个节点,且优先级最高,它就是被侦测出并被选为 victim 的死锁事务。
证明
- 引理 1
s
c
c
1
,
s
c
c
2
∈
S
C
C
scc_1,scc_2 \in SCC
scc1,scc2∈SCC,如果
U
S
G
(
s
c
c
1
)
=
=
U
S
G
(
s
c
c
2
)
USG(scc_1)==USG(scc_2)
USG(scc1)==USG(scc2),则
s
c
c
1
=
=
s
c
c
2
scc_1==scc_2
scc1==scc2
由定义直接验证即可证明。 - 引理 2 如果
s
c
c
2
scc_2
scc2是
s
c
c
1
scc_1
scc1的真上游 SCC,则:
a. s c c 1 ≠ s c c 2 scc_1\ne scc_2 scc1=scc2
b. U S G ( s c c 2 ) ⊂ U S G ( s c c 1 ) USG(scc_2)\subset USG(scc_1) USG(scc2)⊂USG(scc1)
由定义和引理 1 直接证明。
- 定理 3 如果存在一个 SCC,则至少存在一个顶端 SCC
用引理 2 和无穷下降法来证明,考虑到这里集合都是有限的。 - 引理 4
s
c
c
scc
scc是一个顶端 SCC,
X
m
,
X
m
−
1
,
.
.
.
,
X
1
X_m,X_{m-1},...,X_1
Xm,Xm−1,...,X1是
A
S
G
(
s
c
c
)
ASG(scc)
ASG(scc)中
m
(
m
≥
1
)
m(m\ge 1)
m(m≥1)个互不相同的节点,并且
X
m
→
X
m
−
1
→
.
.
.
→
X
1
X_m\to X_{m-1}\to ...\to X_1
Xm→Xm−1→...→X1。假设
X
m
+
1
∈
A
S
G
(
s
c
c
)
X_{m+1}\in ASG(scc)
Xm+1∈ASG(scc)并且
X
m
+
1
(
≠
X
m
)
→
X
m
X_{m+1}(\ne X_m)\to X_m
Xm+1(=Xm)→Xm,则
X
m
+
1
,
X
m
,
.
.
.
,
X
1
X_{m+1},X_m,...,X_1
Xm+1,Xm,...,X1是
m
m
m个互不相同的节点
用反证法证明 - 引理 5
s
c
c
scc
scc是一个顶端 SCC,
X
∈
A
S
G
(
s
c
c
)
,
Y
∈
U
S
G
(
s
c
c
)
,
X
(
≠
Y
)
→
Y
X\in ASG(scc),Y\in USG(scc),X(\ne Y)\to Y
X∈ASG(scc),Y∈USG(scc),X(=Y)→Y,则
A
s
g
D
i
s
t
(
X
,
s
c
c
)
≥
A
s
g
D
i
s
t
(
Y
,
s
c
c
)
+
1
AsgDist(X,scc)\ge AsgDist(Y,scc)+1
AsgDist(X,scc)≥AsgDist(Y,scc)+1
这里暗示了 ASG 中上游节点到 scc 的步长至少要比其直接下游节点到 scc 的步长大 1。 - 引理 6
s
c
c
scc
scc是一个顶端 SCC,
X
∈
A
S
G
(
s
c
c
)
,
A
s
g
D
i
s
t
(
X
,
s
c
c
)
=
m
X\in ASG(scc),AsgDist(X,scc)=m
X∈ASG(scc),AsgDist(X,scc)=m,则
m
≥
1
m\ge 1
m≥1,并且存在一个路径
X
m
(
=
=
X
)
→
X
m
−
1
→
.
.
.
→
X
1
→
X
0
X_{m}(==X)\to X_{m-1}\to ...\to X_1\to X_0
Xm(==X)→Xm−1→...→X1→X0,其中
X
m
,
X
m
−
1
,
.
.
.
,
X
1
X_m,X_{m-1},...,X_1
Xm,Xm−1,...,X1是
A
S
G
(
s
c
c
)
ASG(scc)
ASG(scc)中
m
m
m个互不相同的节点,以及
X
0
∈
s
c
c
X_0\in scc
X0∈scc,且有
A
s
g
D
i
s
t
(
X
j
,
s
c
c
)
=
=
j
,
j
=
0
,
1
,
2
,
.
.
.
,
m
−
1
AsgDist(X_j,scc)==j,j=0,1,2,...,m-1
AsgDist(Xj,scc)==j,j=0,1,2,...,m−1
用上限反向递推可证明,暗示了 ASG 中节点的最长链路上的每个节点都是其最长链路。 - 推论 7
s
c
c
scc
scc是一个顶端 SCC,
∀
X
∈
A
S
G
(
s
c
c
)
,
∃
Y
∈
U
S
G
(
s
c
c
)
\forall X\in ASG(scc),\exists Y\in USG(scc)
∀X∈ASG(scc),∃Y∈USG(scc),使得
X
(
≠
Y
)
→
Y
,
A
s
g
D
i
s
t
(
X
,
s
c
c
)
=
A
s
g
D
i
s
t
(
Y
,
s
c
c
)
+
1
X(\ne Y)\to Y,AsgDist(X,scc)=AsgDist(Y,scc)+1
X(=Y)→Y,AsgDist(X,scc)=AsgDist(Y,scc)+1
由引理 6 直接可得
- 引理 8 在繁殖期,经过
w
w
w轮的繁殖,有:
a. A S G ( s c c ) ASG(scc) ASG(scc)中每个节点的 LCLV 都不再变化
b. ∀ X ∈ A S G ( s c c ) , ∀ Y ∈ U S G ( s c c ) , X ( ≠ Y ) → Y \forall X\in ASG(scc),\forall Y\in USG(scc),X(\ne Y)\to Y ∀X∈ASG(scc),∀Y∈USG(scc),X(=Y)→Y,则 X . L C L V < Y . L C L V X.LCLV<Y.LCLV X.LCLV<Y.LCLV,并且
c. m a x { A S G ( s c c ) . L C L V } < m a x { s c c . L C L V } max\{ASG(scc).LCLV\}<max\{scc.LCLV\} max{ASG(scc).LCLV}<max{scc.LCLV},这里 scc是一个顶端 SCC,并且 w = A s g W i d t h ( s c c ) w=AsgWidth(scc) w=AsgWidth(scc)
a 可用数学归纳法证明,b 和 c 容易从 a 中得出 - 定理 9
s
c
c
scc
scc是一个顶端 SCC,
w
=
A
s
g
W
i
d
t
h
(
s
c
c
)
,
d
=
S
c
c
D
i
a
m
(
s
c
c
)
w=AsgWidth(scc),d=SccDiam(scc)
w=AsgWidth(scc),d=SccDiam(scc),则至少
m
a
x
(
w
,
1
)
max(w,1)
max(w,1)轮繁殖期和
2
×
d
2\times d
2×d轮传播期后,存在唯一的节点
B
∈
U
S
G
(
s
c
c
)
B\in USG(scc)
B∈USG(scc),以及某个节点
A
∈
U
S
G
(
s
c
c
)
A\in USG(scc)
A∈USG(scc),使得
A
(
≠
B
)
→
B
A(\ne B)\to B
A(=B)→B
B . L C L V = A . L C L V B.LCLV=A.LCLV B.LCLV=A.LCLV
B . < P r A P , P r I D > = B . < P u A P , P u I D > = A . < P u A P , P u I D > B.<PrAP,PrID>=B.<PuAP,PuID>=A.<PuAP,PuID> B.<PrAP,PrID>=B.<PuAP,PuID>=A.<PuAP,PuID>
证明思路:引理 8 已经证明了繁殖期后最大 LCLV 只能出现在 scc 内部,在传播期,第一个 d 轮将最大 LCLV 传遍 scc,第二个 d 轮将最大 PrAP 传遍 scc。而 PrAP 的唯一性保证了 B 的唯一性。
注:B 存在于 scc,并在 USG(scc) 中唯一。
参考
- LCL: A Lock Chain Length-based Distributed Algorithm for Deadlock Detection and Resolution
Zhenkun Yang, Chen Qian, Xuwang Teng, Fanyu Kong, Fusheng Han, Quanqing Xu 2023 - https://www.geeksforgeeks.org/hierarchical-deadlock-detection-in-distributed-system/
- A Distributed Algorithm for Deadlock Detection and Resolution.
Don P. Mitchell, Michael J. Merritt, AT&T Bell Laboratories 1984 - Distributed Deadlock Detection
Gilbert K. Cheung December 2004 - Deadlock detection in distributed databases
EDGAR KNAPP 1987