MySQL死锁简单案例演示【存在疑问】

请各位读者对本篇文章采取疑问的态度。

1.环境


  • mysql 5.7
  • windows

2.示例1

mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;
Query OK, 0 rows affected (0.26 sec)
  • Client A起事务,以share 锁模式读取数据
mysql> start transaction;
Query OK, 0 rows affected (0.03 sec)

mysql> SELECT * FROM t WHERE i = 1 lock in SHARE mode;
+------+
| i |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
  • Client B另起事务,删除表t中的数据
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from t;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

上面的这个超时错将会在命令执行一段时间之后,才出现。其超时时间默认设置为50s:

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50 |
+--------------------------+-------+
1 row in set, 1 warning (0.03 sec)

在这个出错命令尚未出现之前,在Client A中继续执行如下命令:

mysql> DELETE FROM t WHERE i = 1;
Query OK, 1 row affected (0.00 sec)

结果在Client B中出现的结果如下:

mysql>  DELETE FROM t WHERE i = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

3.示例2【外键约束至死锁】

  • 建表语句
CREATE TABLE `parent` (
`id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE `child` (
`id` int(11) DEFAULT NULL,
KEY `id_for` (`id`),
CONSTRAINT `id_for` FOREIGN KEY (`id`) REFERENCES `parent` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • 执行SQL
    在session A执行如下命令:
mysql> begin;
Query OK, 0 rows affected (0.03 sec)

mysql> delete from parent where id = 3;
Query OK, 1 row affected (0.01 sec)

在session B执行如下命令:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into child select 3;

发现Session B的回话一直呈阻塞态。

查看​​information_schema.innodb_locks​​表数据,如下:

mysql> select * from information_schema.innodb_locks\G
*************************** 1. row ***************************
lock_id: 21305:145:3:4
lock_trx_id: 21305
lock_mode: S
lock_type: RECORD
lock_table: `insidemysql`.`parent`
lock_index: PRIMARY
lock_space: 145
lock_page: 3
lock_rec: 4
lock_data: 3
*************************** 2. row ***************************
lock_id: 21303:145:3:4
lock_trx_id: 21303
lock_mode: X
lock_type: RECORD
lock_table: `insidemysql`.`parent`
lock_index: PRIMARY
lock_space: 145
lock_page: 3
lock_rec: 4
lock_data: 3
2 rows in set, 1 warning (0.01 sec)

发现出现死锁。

死锁过程很简单:Session A首先发起一个事务(以begin起),接着欲删除parent表中id=3的行记录。这个操作会发起锁操作,申请一个x锁【如上述SQL所示,lock_trx_id为21303,其lock_mode为x型】。然后另外的一个回话Session B发起一个事务(begin起),欲插入一条id=3的记录【如上述SQL所示,lock_trx_id为21305,其lock_mode为s型】。但是因为parent表中的id是child表中的id的外键,所以导致阻塞的产生。注意它们的lock_id 以及lock_data均相同,表明锁住的行记录相同。

4. 参考文章



1.​​https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlock-example.html​​​ 我对本文是有疑问的,因为在Client A中是可以直接删除数据的,而不是Client A陷入了死锁。参考文献对于这个问题可能产生死锁的解释是:


  • Client A 获得一个s锁(并一直持有【用事务保持状态】)
  • Client B请求获得一个x锁(删除某行数据)
  • Client A接着请求一个x锁(也想删除该行数据)
  • 于是Client A,B均陷入死锁



2.《Mysql技术内幕:InnoDB存储引擎》