文章目录
- mysql 死锁
- 死锁演示
- 表结构如下
- 死锁查询mysql 详情
- 命令行 SHOW ENGINE INNODB STATUS
- 如果 两个事务都是按照先更新1 再更新2的顺序去做更新 会发生死锁么?
- 验证一下
- 所以 如果顺序是一致的 不会产生死锁 只会进行等待
- 防止mysql 死锁的方式
- 优化sql 自行顺序 按照统一的顺序进行执行,这样可以解决上面的死锁
- 对上面的事务进行拆分,防止产生死锁。
mysql 死锁
死锁演示
表结构如下
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`user_name` varchar(64) NOT NULL DEFAULT '',`pass_word` varchar(64) NOT NULL DEFAULT '',PRIMARY KEY (`id`),UNIQUE KEY `idx_username` (`user_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;-- 两条数据
INSERT INTO `user` (`id`, `user_name`, `pass_word`) VALUES ('1', 'lock3', '6666');
INSERT INTO `user` (`id`, `user_name`, `pass_word`) VALUES ('2', 'lock4', '66667');
使用navicat 来做示例
开启两个命令行
在第一个命令行执行:
mysql> start transaction;
mysql> update user set user_name = 'updateu1' where id = 1;
在第二个命令行执行
mysql> start transaction;
mysql> update user set user_name = 'updateuser2' where id = 2;
mysql> update user set user_name = 'test1' where id = 1;
此时会出现等待的场景
在第一个命令行执行 会报错提示死锁
mysql> update user set user_name = 'test2' where id = 2;
1213 - Deadlock found when trying to get lock; try restarting transaction
死锁查询mysql 详情
命令行 SHOW ENGINE INNODB STATUS
我们复制出 Status信息进行查看
部分示例如下
=====================================
2024-06-30 14:02:46 0x6f0 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 36 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 64 srv_active, 0 srv_shutdown, 18676 srv_idle
srv_master_thread log flush and writes: 18740
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 3190
OS WAIT ARRAY INFO: signal count 4160
RW-shared spins 0, rounds 8156, OS waits 2615
RW-excl spins 0, rounds 22421, OS waits 53
RW-sx spins 6023, rounds 41833, OS waits 214
Spin rounds per wait: 8156.00 RW-shared, 22421.00 RW-excl, 6.95 RW-sx
------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-06-30 14:00:12 0x4754
*** (1) TRANSACTION:
TRANSACTION 12766497, ACTIVE 31 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 25, OS thread handle 16428, query id 369 localhost 127.0.0.1 root updating
update user set user_name = 'test1' where id = 1
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 36378 page no 3 n bits 80 index PRIMARY of table `demo`.`user` trx id 12766497 lock_mode X locks rec but not gap waiting
Record lock, heap no 13 PHYSICAL RECORD: n_fields 5; compact format; info bits 00: len 4; hex 80000001; asc ;;1: len 6; hex 000000c2cd20; asc ;;2: len 7; hex 3d0000013d044a; asc = = J;;3: len 8; hex 7570646174657531; asc updateu1;;4: len 4; hex 36363636; asc 6666;;*** (2) TRANSACTION:
TRANSACTION 12766496, ACTIVE 55 sec starting index read, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 24, OS thread handle 18260, query id 370 localhost 127.0.0.1 root updating
update user set user_name = 'test2' where id = 2
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 36378 page no 3 n bits 80 index PRIMARY of table `demo`.`user` trx id 12766496 lock_mode X locks rec but not gap
Record lock, heap no 13 PHYSICAL RECORD: n_fields 5; compact format; info bits 00: len 4; hex 80000001; asc ;;1: len 6; hex 000000c2cd20; asc ;;2: len 7; hex 3d0000013d044a; asc = = J;;3: len 8; hex 7570646174657531; asc updateu1;;4: len 4; hex 36363636; asc 6666;;*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 36378 page no 3 n bits 80 index PRIMARY of table `demo`.`user` trx id 12766496 lock_mode X locks rec but not gap waiting
Record lock, heap no 14 PHYSICAL RECORD: n_fields 5; compact format; info bits 00: len 4; hex 80000002; asc ;;1: len 6; hex 000000c2cd21; asc !;;2: len 7; hex 3e0000012a1f5e; asc > * ^;;3: len 11; hex 7570646174657573657232; asc updateuser2;;4: len 5; hex 3636363637; asc 66667;;*** WE ROLL BACK TRANSACTION (2)
------------
TRANSACTIONS
------------
Trx id counter 12766504
Purge done for trx's n:o < 12766504 undo n:o < 0 state: running but idle
History list length 0
根据信息可以查看到 曾经发生锁死锁。有个一事务被回滚了。
如果 两个事务都是按照先更新1 再更新2的顺序去做更新 会发生死锁么?
验证一下
开启2个窗口
1.窗口1 执行:
mysql> start transaction;
mysql> update user set user_name = 'updateu1' where id = 1;
mysql> update user set user_name = 'updateuser2' where id = 2;
- 窗口2执行:
mysql> start transaction;
mysql> update user set user_name = 'updateu1' where id = 1;
执行到这里就开始进行等待了,在等待窗口1 释放锁 无法进行下一句sql的执行了
- 此时窗口1执行
commit;
窗口2才能自行第一句成功。