MySQL锁-死锁举例

Posted 梁小明10000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL锁-死锁举例相关的知识,希望对你有一定的参考价值。

死锁举例

(1)死锁举例-购物车
用户1的购物车:
1. product_id= 100 的商品
2. product_id= 110 的商品
3. product_id= 10 的商品

用户2的购物车:
1. product_id= 10 的商品
2. product_id= 110 的商品
3. product_id= 100 的商品

1. 当一个商品被购买时,其中有一个环节是 库存1 ,即做update操作(set stock=stock-1 where product_id=xx)
2. 当两个用户购买的产品的 product_id 顺序交叉,且两个用户同时下订单时,购物车中的每个商品都会执行库存1的操作,
即个用户在各自的一个事物中做三次update操作,此时即发了死锁(类似AB-BA死锁)。

解决办法
在应用程序端进行修改,即提交购物车订单时,对订单进行排序 ,然后再执行提交。这样只会出现锁等待,而不会出现死锁。

(2)死锁举例
终端1:
"root@localhost:mysql.sock  [burn_test]>set innodb_lock_wait_timeout=60;
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [burn_test]> set tx_isolation="READ-COMMITTED";
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [burn_test]> create table t_lock_8 (a int not null, unique key(a));
Query OK, 0 rows affected (0.05 sec)

"root@localhost:mysql.sock  [burn_test]> insert into t_lock_8 values(1),(10);
Query OK, 2 rows affected (0.02 sec)
Records: 2  Duplicates: 0  Warnings: 0

"root@localhost:mysql.sock  [burn_test]>select * from t_lock_8;
+----+
| a  |
+----+
|  1 |
| 10 |
+----+
2 rows in set (0.00 sec)


"root@localhost:mysql.sock  [burn_test]> begin;
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [burn_test]> insert into t_lock_8 values(8);
Query OK, 1 row affected (0.00 sec)


终端2:
"root@localhost:mysql.sock  [burn_test]> set tx_isolation="READ-COMMITTED";
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [burn_test]> begin;
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [burn_test]> insert into t_lock_8 values(8);
waiting......

终端3:
"root@localhost:mysql.sock  [burn_test]>set tx_isolation="READ-COMMITTED";
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [burn_test]>insert into t_lock_8 values(8);
waiting......

终端4:
"root@localhost:mysql.sock  [(none)]>show engine innodb status\G;
insert into t_lock_8 values(8)
------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585056
 lock mode S locks rec but not gap waitingRecord lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000008; asc     ;;
 1: len 6; hex 0000060e109d; asc       ;;
 2: len 7; hex e7000000520110; asc     R  ;;

------------------
TABLE LOCK table `burn_test`.`t_lock_8` trx id 101585056 lock mode IX
RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585056
 lock mode S locks rec but not gap waitingRecord lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000008; asc     ;;
 1: len 6; hex 0000060e109d; asc       ;;
 2: len 7; hex e7000000520110; asc     R  ;;

---TRANSACTION 101585054, ACTIVE 131 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 80, OS thread handle 140113539970816, query id 711058022 localhost root update
insert into t_lock_8 values(8)
------- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585054
 lock mode S locks rec but not gap waitingRecord lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000008; asc     ;;
 1: len 6; hex 0000060e109d; asc       ;;
 2: len 7; hex e7000000520110; asc     R  ;;

------------------
TABLE LOCK table `burn_test`.`t_lock_8` trx id 101585054 lock mode IX
RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585054
 lock mode S locks rec but not gap waitingRecord lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000008; asc     ;;
 1: len 6; hex 0000060e109d; asc       ;;
 2: len 7; hex e7000000520110; asc     R  ;;

---TRANSACTION 101585053, ACTIVE 174 sec
2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
MySQL thread id 79, OS thread handle 140113539704576, query id 711058003 localhost root
TABLE LOCK table `burn_test`.`t_lock_8` trx id 101585053 lock mode IX
RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585053
 lock_mode X locks rec but not gapRecord lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000008; asc     ;;
 1: len 6; hex 0000060e109d; asc       ;;
 2: len 7; hex e7000000520110; asc     R  ;;
 
终端会话1中的事物持有记录8的Record Lock (X-Lock not gap)
终端会话2中的事物在等待对记录8的S-Lock的授权
终端会话3中的事物在等待对记录8的S-Lock的授权


 
 
终端1:回滚
"root@localhost:mysql.sock  [burn_test]>rollback;
Query OK, 0 rows affected (0.01 sec)

终端2:插入成功
"root@localhost:mysql.sock  [burn_test]> insert into t_lock_8 values(8);
Query OK, 1 row affected (5.24 sec)


终端3:发送死锁
"root@localhost:mysql.sock  [burn_test]>insert into t_lock_8 values(8);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1136, 4 row lock(s)
MySQL thread id 80, OS thread handle 140113539970816, query id 711058024 localhost root update
insert into t_lock_8 values(8)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585054
 lock_mode X locks gap before rec insert intention waitingRecord lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 0000060e1098; asc       ;;
 2: len 7; hex e400000051011c; asc     Q  ;;

*** (2) TRANSACTION:
TRANSACTION 101585057, ACTIVE 3 sec inserting, thread declared inside InnoDB 1
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 81, OS thread handle 140113310545664, query id 711058025 localhost root update
insert into t_lock_8 values(8)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585057
 lock mode S locks gap before recRecord lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 0000060e1098; asc       ;;
 2: len 7; hex e400000051011c; asc     Q  ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585057
 lock_mode X locks gap before rec insert intention waitingRecord lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 0000060e1098; asc       ;;
 2: len 7; hex e400000051011c; asc     Q  ;;

*** WE ROLL BACK TRANSACTION (2)
------------
TRANSACTIONS
------------
Trx id counter 101585062
Purge done for trx's n:o < 101585062 undo n:o < 0 state: running but idle
History list length 19
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421595533284752, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421595533283840, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421595533282016, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421595533281104, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 101585054, ACTIVE 517 sec
4 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 1
MySQL thread id 80, OS thread handle 140113539970816, query id 711058024 localhost root
TABLE LOCK table `burn_test`.`t_lock_8` trx id 101585054 lock mode IX
RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585054
 lock mode S locks rec but not gapRECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585054
 lock mode S locks gap before recRecord lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 0000060e1098; asc       ;;
 2: len 7; hex e400000051011c; asc     Q  ;;

Record lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000008; asc     ;;
 1: len 6; hex 0000060e109e; asc       ;;
 2: len 7; hex e8000000520110; asc     R  ;;

RECORD LOCKS space id 85 page no 3 n bits 72 index a of table `burn_test`.`t_lock_8` trx id 101585054
 lock_mode X locks gap before rec insert intentionRecord lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 0000060e1098; asc       ;;
 2: len 7; hex e400000051011c; asc     Q  ;;


死锁分析:
原来加在记录8上的锁,都转移到了记录10上了( 锁继承 )
1. 因为终端会话1中的事物回滚了,则记录8标记为删除(delete-mark)
2. 而终端会话2和终端会话3中的事物都对记录8添加了 S-Lock ( 后台 Purge 线程会把记录8给删除 )
3. 此时下一个记录,即记录10会继承之前记录8上的锁,所以上面看到了锁的信息都在记录10上了

则此时的锁的状态是:
1. 终端会话2持有记录10的S Gap-Lock ,并且等待 X locks gap insert intention lock
2. 终端会话3持有记录10的 S Gap-Lock ,并且等待X locks gap insert intention lock


X Lock 和 S Lock 是不兼容的
终端会话3的insert intention lock 等待终端会话2的S Gap-Lock 的释放;
终端会话2的insert intention lock 等待终端会话3的SGap-Lock 的释放;
所以产生了死锁( 其实还是 AB-BA 死锁 )。



备注:个人学习整理,仅用于学习,如有侵权,联系马上删除!

以上是关于MySQL锁-死锁举例的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 锁原理通过 6 个死锁案例,让你彻底理解 MySQL 锁机制,死锁的原因!

锁机制

MySql 死锁

MySql 死锁

将共享锁升级为独占锁时避免 MySQL 死锁

mysql 死锁简单分析