代码地址:GitHub - zhangqingqing24630/MySQL-
此项目来源于何人听我楚狂声
目录
本节将收尾 VM 层,以及mysql 如何检测 2PL 导致的死锁,还介绍了一些上一章节没有介绍到的方法。
死锁
死锁的造成
在mysql中,两阶段锁协议(2PL)通常包括扩张和收缩两个阶段。在扩张阶段,事务将获取锁,但不能释放任何锁。在收缩阶段,可以释放现有的锁,但不能获取新的锁,这种规定存在着死锁的风险。
例如:当T1’在扩张阶段,获取了Y的读锁,并读取了Y,此时想要去获取X的写锁,却发现T2’的读锁锁定了X,而T2’也想要获取Y的写锁。简而言之,T1’不得到X是不会释放Y的,T2’不得到Y也是不会释放X的,这便陷入了循环,便形成了死锁。
死锁检测
前面提到了 2PL 会阻塞事务,直至持有锁的线程释放锁。可以将这种等待关系抽象成有向边,例如 Tj 在等待 Ti,就可以表示为 Tj --> Ti。这样,无数有向边就可以形成一个图(不一定是连通图)。检测死锁也就简单了,只需要查看这个图中是否有环即可。
创建一个 LockTable 对象,在内存中维护这张图。维护结构如下:
public class LockTable {
private Map<Long, List<Long>> x2u; // 某个XID已经获得的资源的UID列表
private Map<Long, Long> u2x; // UID被某个XID持有
private Map<Long, List<Long>> wait; // 正在等待UID的XID列表
private Map<Long, Lock> waitLock; // 正在等待资源的XID的锁
private Map<Long, Long> waitU; // XID正在等待的UID
private Lock lock;
...
}
在每次出现等待的情况时,就尝试向图中增加一条边,并进行死锁检测。如果检测到死锁,就撤销这条边,不允许添加,并撤销该事务。
// 不需要等待则返回null,否则返回锁对象
// 会造成死锁则抛出异常
public Lock add(long xid, long uid) throws Exception {
lock.lock();
try {
//某个xid已经获得的资源的UID列表,如果在这个列表里面,则不造成死锁,也不需要等待
if(isInList(x2u, xid, uid)) {//x2u xid list<uid>
return null;
}
//这里表示有了一个新的uid,则把uid加入到u2x和x2u里面,不死锁,不等待
//u2x uid被某个xid占有
if(!u2x.containsKey(uid)) {//uid xid
u2x.put(uid, xid);
putIntoList(x2u, xid, uid);
return null;
}
//以下就是需要等待的情况
//多个事务等待一个uid的释放
waitU.put(xid, uid);//把需要等待uid的xid添加到等待列表里面
//System.out.println("waitU"+waitU);
putIntoList(wait,

本文深入探讨了MySQL中两阶段锁协议(2PL)引发的死锁问题,详细阐述了死锁的成因,并通过代码示例展示了死锁检测的算法。在检测到死锁时,系统会撤销事务以解除死锁状态。此外,文章还介绍了如何在内存中维护等待图以辅助死锁检测,并分析了具体的检测流程。
最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



