1. 程式人生 > >MySQL死鎖簡單案例演示

MySQL死鎖簡單案例演示

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儲存引擎》