测试用表
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `id_c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `t` VALUES ('0', '0', '0');
INSERT INTO `t` VALUES ('5', '5', '5');
INSERT INTO `t` VALUES ('10', '10', '10');
INSERT INTO `t` VALUES ('15', '15', '15');
INSERT INTO `t` VALUES ('20', '20', '20');
INSERT INTO `t` VALUES ('25', '25', '25');
开启两个会话。一般死锁都是发生在长事务中的。
模拟死锁
事务A的所有操作
事务B的所有操作
时序流程
- 事务A begin 事务B begin
- 事务A 执行select 对 5,15 加读锁 事务B 对 10 20 加读锁
- 事务B 想要修改id = 15 也就是对15加写锁。此时事务B会被阻塞。
- 事务A 想要修改id = 10 对10加写锁,此时事务A回滚。
分析:
事务A如果不回滚会怎样?
事务A持有 5 15 读锁,事务B持有 10 20 读锁
事务B 要去获取 5的写锁 事务A要获取 10的写锁。
完全满足了死锁的四个必要条件
- 互斥占有共享资源
- 不可剥夺
- 循环并等待
- 持有并等待
Mysql解决就是
1、让一个影响少的事务回滚
2、 超时等待。
补充一下上面死锁的产生的前提条件
- 读锁和写锁是互斥的
- 事务中锁的两阶段提交
- 需要查询或者修改的时候才加锁
- 事务结束的时候释放锁
为什么要用两阶段锁?
我搜到的是为了解决隔离性,并发事务中保证事务的调度正确。
一般长事务更加容易导致死锁的发生。要避免长事务死锁的发生,怎么避免?
- 尽量将锁的获取放到事务的后面,减少锁等待时间
- 控制并发度