NOTE

数据库死锁

1. 什么是死锁 死锁是指两个(或多个)事务相互持有对方想要的锁 如果事务 1 在表 A 上获得一个排他锁,同时试图获取一个在表 B 上的排他锁, 而事务 2 已经持有表 B 的排他锁,同时却正在请求表 A 上的一个排他锁,那么两个事务就都不能进行下去。 1.1. 例子 两个并发事务在修改一个表。 第一个事务执行: 这样就在acctnum = 11111的行上获得了一个行级锁 然后,第二个事务执行: 第一个UPDATE语句成功地在指定行上获得了一个行级锁acctnum = 22222,因此它成功更新了该行 但是第二个UPDATE语句发现它试图更新的行已经被锁住了,因此它等待持有该锁的事务结束

Database创建于 更新于 约 2 分钟读完historical

这是历史学习笔记,可能存在过时或不完整的理解。

1. 什么是死锁

死锁是指两个(或多个)事务相互持有对方想要的锁 如果事务 1 在表 A 上获得一个排他锁,同时试图获取一个在表 B 上的排他锁, 而事务 2 已经持有表 B 的排他锁,同时却正在请求表 A 上的一个排他锁,那么两个事务就都不能进行下去。

1.1. 例子

两个并发事务在修改一个表。 第一个事务执行:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111;

这样就在acctnum = 11111的行上获得了一个行级锁 然后,第二个事务执行:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222;
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;

第一个UPDATE语句成功地在指定行上获得了一个行级锁acctnum = 22222,因此它成功更新了该行 但是第二个UPDATE语句发现它试图更新的行已经被锁住了,因此它等待持有该锁的事务结束 事务一执行:

UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;

事务一试图在指定行上获得一个行级锁,但是事务二持有了这把锁。因此,事务一被事务二阻塞,而事务二也被事务一阻塞,这就是死锁。

2. 如何解决死锁

2.1. 超时

2.1.1. 是什么

等待,直到超时 比如MySQL InnoDB的超时时间由innodb_lock_wait_timeout来设置

2.1.2. 问题

时间不好确定

2.2. 死锁检测

2.2.1. 是什么

发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行 比如MySQL InnoDB把innodb_deadlock_detect参数设置为on可以开启这个逻辑。开启之后MySQL加锁的时候,发现冲突,会进行死锁检测,就是在wait-for graph里面看有没有环(参考链表中环的入口结点.md)。如下:将事务及持有的锁作为顶点,请求的锁作为边,构造图。如果图中有环那么就有死锁 MySQL死锁

一旦发现死锁会把死锁记录下来,并选择牺牲一个事务进行回滚

2.2.2. 问题

如果所有事务更新同一行,每个新来的线程都要判断下会不会由于自己的加入导致了死锁,效率O(N),太耗费CPU了

3. 参考

  • 显式锁定
  • 数据库并发死锁有何种原因和解决方案?(内部链接已脱敏)

讨论

使用 GitHub 账号参与讨论,评论会保存在 GitHub Issues 中。在 GitHub 查看