mysql> select * from books;
+----+------------------------+-------+-------------+---------------------+
| Id | Title | Price | PublishDate | ModifyDate |
+----+------------------------+-------+-------------+---------------------+
| 1 | Java编程思想 | 98.50 | 2005-01-02 | 2021-01-08 14:50:48 |
| 2 | HeadFirst设计模式 | 55.70 | 2010-11-09 | 2021-01-08 14:51:17 |
| 3 | 第一行Android代码 | 69.90 | 2015-06-23 | 2021-01-08 14:51:48 |
| 4 | C++编程思想 | 88.50 | 2004-01-09 | 2021-01-08 14:52:15 |
| 5 | HeadFirstJava | 55.70 | 2013-12-17 | 2021-01-08 14:52:43 |
| 6 | 疯狂Android | 19.50 | 2014-07-31 | 2021-01-08 14:53:27 |
+----+------------------------+-------+-------------+---------------------+
6 rows in set (0.00 sec)
SessionA:
mysql> delete from books where id=6;
Query OK, 1 row affected (0.06 sec)
SessionB:
mysql> delete from books where id=1;
Query OK, 1 row affected (0.00 sec)
死锁比较简单理解,就是两个或者多个事务之间相互加锁,因为事务加锁算法是逐行加锁的. 这就导致你加了前面几行,我加了后面几行,你我相遇表中间这几行. 请问是你让我呢? 还是我让你? 不好意思事务是不讲武德的.
MYSQL RR隔离级别上 依旧主键 单语句,单行,外加上自动提交功能,基本不会遇到死锁. 死锁主要集中在多语句的事务组成和加了GAP锁
mysql> select * from books;
+----+------------------------+-------+-------------+---------------------+
| Id | Title | Price | PublishDate | ModifyDate |
+----+------------------------+-------+-------------+---------------------+
| 2 | HeadFirst设计模式 | 55.70 | 2010-11-09 | 2021-01-08 14:51:17 |
| 3 | 第一行Android代码 | 69.90 | 2015-06-23 | 2021-01-08 14:51:48 |
| 4 | C++编程思想 | 88.50 | 2004-01-09 | 2021-01-08 14:52:15 |
| 5 | HeadFirstJava | 55.70 | 2013-12-17 | 2021-01-08 14:52:43 |
+----+------------------------+-------+-------------+---------------------+
4 rows in set (0.00 sec)
添加回这两条记录
事务开启的时候 BEGIN; START TRANSACTION; 需要人工提交
SessionA:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM books WHERE id=2;
Query OK, 1 row affected (0.00 sec)
<==SessionB: 插入会话B 两个会话交叉执行
mysql> DELETE FROM books WHERE id=4;
Query OK, 1 row affected (0.00 sec)
mysql> DELETE FROM books WHERE id=5;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
mysql>
SessionB:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM books WHERE id=5;
Query OK, 1 row affected (0.00 sec)
mysql> DELETE FROM books WHERE id=3;
Query OK, 1 row affected (0.00 sec)
mysql> DELETE FROM books WHERE id=7;
Query OK, 1 row affected (0.00 sec)
mysql> DELETE FROM books WHERE id=8;
Query OK, 1 row affected (0.00 sec)
mysql> DELETE FROM books WHERE id=4;
Query OK, 1 row affected (0.00 sec)
mysql> DELETE FROM books WHERE id=2;
Query OK, 1 row affected (0.00 sec)
mysql>
会话A 加了记录锁 ID=(2,4) 欲加5
会话B 加了记录锁 ID=(5,3,7,8) 欲加4
死锁日志
SHOW ENGINE INNODB STATUS;
------------------------
LATEST DETECTED DEADLOCK
------------------------
2021-01-08 15:21:01 0x7f726e9dd700
*** (1) TRANSACTION:
TRANSACTION 2094, ACTIVE 377 sec starting index read <==SessionA:
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 2
MySQL thread id 16, OS thread handle 140129549526784, query id 452 localhost root updating
DELETE FROM books WHERE id=5
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 2 page no 4 n bits 80 index PRIMARY of table `bookstore`.`books` trx id 2094 lock_mode X locks rec but not gap waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d0151; asc Q;;
3: len 13; hex 4865616446697273744a617661; asc HeadFirstJava;;
4: len 5; hex 8000003746; asc 7F;;
5: len 3; hex 8fbb91; asc ;;
6: len 4; hex 5ff8013b; asc _ ;;;
*** (2) TRANSACTION:
TRANSACTION 2095, ACTIVE 172 sec starting index read <==SessionB:
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 4
MySQL thread id 17, OS thread handle 140129458837248, query id 453 localhost root updating
DELETE FROM books WHERE id=4
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 2 page no 4 n bits 80 index PRIMARY of table `bookstore`.`books` trx id 2095 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000007; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d01d8; asc ;;
3: len 13; hex e796afe78b82416e64726f6964; asc Android;;
4: len 5; hex 8000001332; asc 2;;
5: len 3; hex 8fbcff; asc ;;
6: len 4; hex 5ff804d3; asc _ ;;
Record lock, heap no 4 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000003; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d0190; asc ;;
3: len 22; hex e7acace4b880e8a18c416e64726f6964e4bba3e7a081; asc Android ;;
4: len 5; hex 800000455a; asc EZ;;
5: len 3; hex 8fbed7; asc ;;
6: len 4; hex 5ff80104; asc _ ;;
Record lock, heap no 6 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d0151; asc Q;;
3: len 13; hex 4865616446697273744a617661; asc HeadFirstJava;;
4: len 5; hex 8000003746; asc 7F;;
5: len 3; hex 8fbb91; asc ;;
6: len 4; hex 5ff8013b; asc _ ;;;
Record lock, heap no 8 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000008; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d0217; asc ;;
3: len 16; hex 4a617661e7bc96e7a88be6809de683b3; asc Java ;;
4: len 5; hex 8000006232; asc b2;;
5: len 3; hex 8faa22; asc ";;
6: len 4; hex 5ff804f4; asc _ ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 2 page no 4 n bits 80 index PRIMARY of table `bookstore`.`books` trx id 2095 lock_mode X locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000004; asc ;;
1: len 6; hex 00000000082e; asc .;;
2: len 7; hex 010000011c0198; asc ;;
3: len 15; hex 432b2be7bc96e7a88be6809de683b3; asc C++ ;;
4: len 5; hex 8000005832; asc X2;;
5: len 3; hex 8fa829; asc );;
6: len 4; hex 5ff8011f; asc _ ;;
*** WE ROLL BACK TRANSACTION (1)
这个死锁日志 稍微细看下就能看得50%的明白,不像ORACLE样!
从头往下看
TRANSACTION 2094, ACTIVE 377 sec starting index read <==SessionA:
这句说明 是事务2094 也就是我们的事务A
MySQL thread id 16, OS thread handle 140129549526784, query id 452 localhost root updating
DELETE FROM books WHERE id=5
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
这点信息是说 线程ID OS线程 QUERY ID 以及 语句
下面是 等什么样的锁
RECORD LOCKS 表示记录锁,space id为2,page号4 ,n bits 80表示这个聚集索引记录锁结构上留有80个Bit位
RECORD LOCKS space id 2 page no 4 n bits 80 index PRIMARY of table `bookstore`.`books` trx id 2094 lock_mode X locks rec but not gap waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d0151; asc Q;;
3: len 13; hex 4865616446697273744a617661; asc HeadFirstJava;;
4: len 5; hex 8000003746; asc 7F;;
5: len 3; hex 8fbb91; asc ;;
6: len 4; hex 5ff8013b; asc _ ;;;
其中这段就是行记录; 0 是聚集索引的值,1是事务ID; 2是UNDO地址;3TITLE;
4 价格; 5 发行日期 6 修改日期
LEN 表示字节长度; ASC 翻译成ASCII码 是啥?
0: len 4; hex 80000005; asc ;; ##主键
1: len 6; hex 00000000082f; asc /;; ##事务ID
2: len 7; hex 020000011d0151; asc Q;; ## UNDO地址
3: len 13; hex 4865616446697273744a617661; asc HeadFirstJava;;
4: len 5; hex 8000003746; asc 7F;;
5: len 3; hex 8fbb91; asc ;;
6: len 4; hex 5ff8013b; asc _ ;;;
看下事务B 绿色是我人工写上去的. 会话B 锁了记录(7,3,5,8)
*** (2) TRANSACTION:
TRANSACTION 2095, ACTIVE 172 sec starting index read <==SessionB:
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 4
MySQL thread id 17, OS thread handle 140129458837248, query id 453 localhost root updating
DELETE FROM books WHERE id=4
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 2 page no 4 n bits 80 index PRIMARY of table `bookstore`.`books` trx id 2095 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000007; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d01d8; asc ;;
3: len 13; hex e796afe78b82416e64726f6964; asc Android;;
4: len 5; hex 8000001332; asc 2;;
5: len 3; hex 8fbcff; asc ;;
6: len 4; hex 5ff804d3; asc _ ;;
Record lock, heap no 4 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000003; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d0190; asc ;;
3: len 22; hex e7acace4b880e8a18c416e64726f6964e4bba3e7a081; asc Android ;;
4: len 5; hex 800000455a; asc EZ;;
5: len 3; hex 8fbed7; asc ;;
6: len 4; hex 5ff80104; asc _ ;;
Record lock, heap no 6 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d0151; asc Q;;
3: len 13; hex 4865616446697273744a617661; asc HeadFirstJava;;
4: len 5; hex 8000003746; asc 7F;;
5: len 3; hex 8fbb91; asc ;;
6: len 4; hex 5ff8013b; asc _ ;;;
Record lock, heap no 8 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000008; asc ;;
1: len 6; hex 00000000082f; asc /;;
2: len 7; hex 020000011d0217; asc ;;
3: len 16; hex 4a617661e7bc96e7a88be6809de683b3; asc Java ;;
4: len 5; hex 8000006232; asc b2;;
5: len 3; hex 8faa22; asc ";;
6: len 4; hex 5ff804f4; asc _ ;;
欲要加锁 记录ID=4上 加记录锁X NOT GAP
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 2 page no 4 n bits 80 index PRIMARY of table `bookstore`.`books` trx id 2095 lock_mode X locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 7; compact format; info bits 32
0: len 4; hex 80000004; asc ;;
1: len 6; hex 00000000082e; asc .;;
2: len 7; hex 010000011c0198; asc ;;
3: len 15; hex 432b2be7bc96e7a88be6809de683b3; asc C++ ;;
4: len 5; hex 8000005832; asc X2;;
5: len 3; hex 8fa829; asc );;
6: len 4; hex 5ff8011f; asc _ ;;
死锁日志不会显示事务A的持有什么锁! 那么我们要用8.0的DATA_LOCKS
mysql> SELECT OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA
-> FROM performance_schema.data_locks;
+-------------+------------+-----------+---------------+-------------+-----------+
| OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+-------------+------------+-----------+---------------+-------------+-----------+
| books | NULL | TABLE | IX | GRANTED | NULL |
| books | PRIMARY | RECORD | X,REC_NOT_GAP | GRANTED | 2 |
| books | PRIMARY | RECORD | X,REC_NOT_GAP | GRANTED | 4 |
+-------------+------------+-----------+---------------+-------------+-----------+
删除不存在的数据造成的死锁
SessionA:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM books WHERE id=2;
<<==SessionB:start hear
mysql> INSERT INTO books values(2,'HeadFirst设计模式',55.70, date('2010-11-09'));
SessionB:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM books WHERE id=4;
Query OK, 1 row affected (0.00 sec)
INSERT INTO books values(4,'C++编程思想',88.70, date('2004-01-09'));
假设表有记录1,5
SESSIONA :ID=2不存在 那么会加锁GAP X (1,5)范围
SESSIONB :ID=4不存在 那么会加锁GAP X (1,5)范围
INSERT ID=2 会发生锁等待 sessionB 释放锁 记录5
INSERT ID=4 会发生锁等待 sessionA 释放锁 记录5
RR隔离下GAP 加锁范围比较大 (1,5) (X,5) (5,+00)
插入意向死锁
mysql> CREATE TABLE T (A INT UNSIGNED NOT NULL PRIMARY KEY ,B INT);
mysql> INSERT INTO T VALUES(10,0),(20,0);
SessionA:
mysql> BEGIN;
mysql> UPDATE T SET B=1 WHERE A=20;
<<=SESSION BEGIN; SELECT * FROM T LOCK IN SHARE MODE;
mysql> INSERT INTO T VALUES(11,1);
SESSION B:
mysql> BEGIN;
mysql> SELECT * FROM T LOCK IN SHARE MODE;
<<==当SESSIONA 执行INSERT INTO T VALUES(11,1);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
mysql> SELECT OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA FROM performance_schema.data_locks;
+-------------+------------+-----------+---------------+-------------+-----------+
| OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+-------------+------------+-----------+---------------+-------------+-----------+
| T | NULL | TABLE | IX | GRANTED | NULL |
| T | PRIMARY | RECORD | X,REC_NOT_GAP | GRANTED | 20 |
| T | NULL | TABLE | IS | GRANTED | NULL |
| T | PRIMARY | RECORD | S | GRANTED | 10 |
| T | PRIMARY | RECORD | S | WAITING | 20 |
+-------------+------------+-----------+---------------+-------------+-----------+
从这里看出 SESSION A 第一个UPDATE语句 IX + X 记录
SESSION B 共享锁 锁定 ID=(10,20 ) NEXT-KEY LOCK (S)
SESSION A 阻塞了SESSION B
SESSION A 执行插入语句后 立马SESSION B死锁你看 OVER后
mysql> SELECT OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA FROM performance_schema.data_locks;
+-------------+------------+-----------+------------------------+-------------+-----------+
| OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+-------------+------------+-----------+------------------------+-------------+-----------+
| T | NULL | TABLE | IX | GRANTED | NULL |
| T | PRIMARY | RECORD | X,REC_NOT_GAP | GRANTED | 20 |
| T | PRIMARY | RECORD | X,GAP,INSERT_INTENTION | GRANTED | 20 |
+-------------+------------+-----------+------------------------+-------------+-----------+
看到 INSERT INTO T VALUES(11,1); X,GAP,INSERT_INTENTION
----------+------------+-----------+---------------+-------------+-----------+
死锁日志
------------------------
LATEST DETECTED DEADLOCK
------------------------
2021-01-08 16:15:55 0x7f727405a700
*** (1) TRANSACTION:
TRANSACTION 421605069725968, ACTIVE 35 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 17, OS thread handle 140129458837248, query id 486 localhost root executing
SELECT * FROM T LOCK IN SHARE MODE
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 3 page no 4 n bits 72 index PRIMARY of table `bookstore`.`T` trx id 421605069725968 lock mode S waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 00000014; asc ;;
1: len 6; hex 00000000084f; asc O;;
2: len 7; hex 01000001230151; asc # Q;;
3: len 4; hex 80000001; asc ;;
*** (2) TRANSACTION:
TRANSACTION 2127, ACTIVE 90 sec inserting
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 16, OS thread handle 140129549526784, query id 488 localhost root update
INSERT INTO T VALUES(11,1)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 3 page no 4 n bits 72 index PRIMARY of table `bookstore`.`T` trx id 2127 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 00000014; asc ;;
1: len 6; hex 00000000084f; asc O;;
2: len 7; hex 01000001230151; asc # Q;;
3: len 4; hex 80000001; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 3 page no 4 n bits 72 index PRIMARY of table `bookstore`.`T` trx id 2127 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 00000014; asc ;;
1: len 6; hex 00000000084f; asc O;;
2: len 7; hex 01000001230151; asc # Q;;
3: len 4; hex 80000001; asc ;;
*** WE ROLL BACK TRANSACTION (1)
SEESION B 加锁范围 (-00,10],(10,20],[20,+00)
SEESION A SEESION B
A=20 lock(x) 记录X
A=10 LOCK (S) ;A=20 LOCK(S) waite SESSION A ; NEXTKEY LOCK(S)
INSERT INTO T VALUES(11,1);
这三个锁基本上都不兼容
其他锁兼容
锁模式:
选择UNDO资源少的回话被KILL掉