成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

MySQL 核心模塊—揭秘死鎖案例分析插入意向鎖

數據庫 MySQL
沒有其它事務在等待獲得這條記錄的鎖,事務 1 想要往這條記錄前面的間隙插入記錄,不需要等待獲得插入意向鎖,可以直接插入記錄。

1. 準備工作

創建測試表:

CREATE TABLE `t_deadlock_1` (
  `id` int NOT NULL AUTO_INCREMENT,
  `i1` int DEFAULT NULL,
  `i2` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_i1` (`i1`)
) ENGINE = InnoDB;

插入測試數據:

INSERT INTO `t_deadlock_1` (`id`, `i1`, `i2`)
VALUE (22, 2, 3), (23, 5, 4), (24, 6, 7);

把事務隔離級別設置為 REPEATABLE-READ(如已設置,忽略此步驟):

SET transaction_isolation = 'REPEATABLE-READ';

-- 確認設置成功
SHOW VARIABLES like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+

2. 加鎖情況

創建 2 個 MySQL 連接,開啟 2 個事務,執行以下 SQL:

-- session 1(事務 1)
BEGIN; DELETE FROM t_deadlock_1 WHERE `i1` = 5;

-- session 2(事務 2)
BEGIN; DELETE FROM t_deadlock_1 WHERE `i1` = 5;

在 session 1 中執行以下 select 語句查看加鎖情況:

select
  engine_transaction_id, object_name, index_name,
  lock_type, lock_mode, lock_status, lock_data
from performance_schema.data_locks
where object_name = 't_deadlock_1'
and lock_type = 'RECORD'\G

***************************[ 1. row ]***************************
engine_transaction_id | 250490
object_name           | t_deadlock_1
index_name            | idx_i1
lock_type             | RECORD
lock_mode             | X
lock_status           | WAITING
lock_data             | 5, 23
***************************[ 2. row ]***************************
engine_transaction_id | 250489
object_name           | t_deadlock_1
index_name            | idx_i1
lock_type             | RECORD
lock_mode             | X
lock_status           | GRANTED
lock_data             | 5, 23
***************************[ 3. row ]***************************
engine_transaction_id | 250489
object_name           | t_deadlock_1
index_name            | PRIMARY
lock_type             | RECORD
lock_mode             | X,REC_NOT_GAP
lock_status           | GRANTED
lock_data             | 23
***************************[ 4. row ]***************************
engine_transaction_id | 250489
object_name           | t_deadlock_1
index_name            | idx_i1
lock_type             | RECORD
lock_mode             | X,GAP
lock_status           | GRANTED
lock_data             | 6, 24

加鎖情況第 2 ~ 4 條,是事務 1 的加鎖情況。

事務 1 執行 delete 語句過程中,會先掃描需要刪除的記錄,并對掃描到的記錄加鎖。

掃描過程使用了二級索引 idx_i1,先定位到這個索引中 <i1 = 5, id = 23> 的記錄,加排他 Next-Key 鎖,對應加鎖情況第 2 條(2. row)。

回表查詢主鍵索引中 <id = 23> 的記錄,加排他普通記錄鎖,對應加鎖情況第 3 條(3. row)。

掃描到匹配 where 條件的第 1 條記錄之后,接著掃描下一條記錄,也就是二級索引 idx_i1 中 <i1 = 6, id = 24> 的記錄,加排他間隙鎖,對應加鎖情況第 4 條(4. row)。

因為這條記錄不匹配 where 條件,不需要回表查詢對應的主鍵索引記錄,所以沒有對主鍵索引中 <id = 24> 的記錄加鎖。

按照 <i1 = 5, id = 23> 的記錄加鎖情況,<i1 = 6, id = 24> 的記錄也應該加排他 Next-Key 鎖,但實際上只加了排他間隙鎖。

這是因為 InnoDB 對命中索引的等值查詢條件做了特殊處理。

可重復讀隔離級別默認會對掃描到的記錄加排他 Next-Key 鎖。如果 InnoDB 發現記錄不匹配命中索引的等值查詢條件,會改為對這條記錄加排他間隙鎖,避免鎖定不匹配的記錄本身,以縮小加鎖范圍。

加鎖情況第 1 條(1. row),是事務 2 的加鎖情況。

事務 2 執行 delete 語句過程中,也會先掃描需要刪除的記錄,并對掃描到的記錄加鎖。

掃描過程同樣使用了二級索引 idx_i1,先定位到這個索引中 <i1 = 5, id = 23> 的記錄,加排他 Next-Key 鎖。

但是,因為事務 1 先對這條記錄加了排他 Next-Key 鎖,事務 2 的加鎖操作被阻塞,進入鎖等待狀態。

介紹完事務 1 和事務 2 的加鎖情況,我們再在 session 1 中執行以下 insert 語句,插入一條記錄:

INSERT INTO t_deadlock_1 (`id`, `i1`, `i2`) VALUES (25, 2, 10);

結果就出現了死鎖,事務 2 被選擇成為死鎖受害事務,回滾了:

(1213, 'Deadlock found when trying to get lock; try restarting transaction')

3. 死鎖分析

為了找到死鎖原因,我們需要借助死鎖日志,可以在 session 1 或者 session 2 中執行以下 show 語句,查看最新的死鎖日志:

SHOW ENGINE InnoDB STATUS\G

------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-09-07 07:48:49 0x7000087c0000
*** TRANSACTION:
-- 事務 2
TRANSACTION 250490, ACTIVE 19 sec starting index read
...
DELETE FROM t_deadlock_1 WHERE `i1` = 5

*** HOLDS THE LOCK(S):
RECORD LOCKS space id 232 page no 5 n bits 72 \
  index idx_i1 of table `test`.`t_deadlock_1` trx id 250490 \
  lock_mode X waiting
Record lock, heap no 3 PHYSICAL RECORD: \
  n_fields 2; compact format; info bits 32
 0: len 4; hex 80000005; asc     ;;
 1: len 4; hex 80000017; asc     ;;


*** WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 232 page no 5 n bits 72 \
  index idx_i1 of table `test`.`t_deadlock_1` trx id 250490 \
  lock_mode X waiting
Record lock, heap no 3 PHYSICAL RECORD: \
  n_fields 2; compact format; info bits 32
 0: len 4; hex 80000005; asc     ;;
 1: len 4; hex 80000017; asc     ;;


*** TRANSACTION:
-- 事務 1
TRANSACTION 250489, ACTIVE 26 sec inserting
...
INSERT INTO t_deadlock_1 (`id`, `i1`, `i2`) VALUES (25, 2, 10)

*** HOLDS THE LOCK(S):
RECORD LOCKS space id 232 page no 5 n bits 72 \
  index idx_i1 of table `test`.`t_deadlock_1` trx id 250489 \
  lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: \
  n_fields 2; compact format; info bits 32
 0: len 4; hex 80000005; asc     ;;
 1: len 4; hex 80000017; asc     ;;


*** WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 232 page no 5 n bits 72 \
  index idx_i1 of table `test`.`t_deadlock_1` trx id 250489 \
  lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: \
  n_fields 2; compact format; info bits 32
 0: len 4; hex 80000005; asc     ;;
 1: len 4; hex 80000017; asc     ;;

以上是從 SHOW ENGINE InnoDB STATUS 結果中摘出來的最新的死鎖日志。

為了方便手機上閱讀,我對格式做了一些調整,內容也有一點小小的修改,去掉了事務前面的編號。

從死鎖日志可以看到,事務 1(250489)和事務 2(250490)加鎖發生死鎖,都是因為二級索引 idx_i1 中的一條記錄:

/* i1 字段 */ 0: len 4; hex 80000005; asc     ;;
/* id 字段 */ 1: len 4; hex 80000017; asc     ;;

在 《30. 死鎖日志詳解》這篇文章中,我們介紹過把死鎖日志中整數類型字段值轉換為整數的方法。

我們用這個方法,把上面死鎖日志中這條記錄的兩個字段值轉換為整數:

## i1 字段,輸出:5
echo $((0x80000005 ^ (1 << (4 * 8 - 1))))

## id 字段,輸出:23
echo $((0x80000017 ^ (1 << (4 * 8 - 1))))

從以上輸出可以看到,事務 1(250489)和事務 2(250490)加鎖發生死鎖,都是因為二級索引 idx_i1 中 <i1 = 5, id = 23> 的記錄。

*** TRANSACTION:
-- 事務 1
TRANSACTION 250489, ACTIVE 26 sec inserting
...
*** HOLDS THE LOCK(S):
RECORD LOCKS space id 232 page no 5 n bits 72 \
  index idx_i1 of table `test`.`t_deadlock_1` trx id 250489 \
  lock_mode X
...
*** WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 232 page no 5 n bits 72 \
  index idx_i1 of table `test`.`t_deadlock_1` trx id 250489 \
  lock_mode X locks gap before rec insert intention waiting
...

上面是從死鎖日志中摘出來的一小段,從這段日志可以看到,事務 1(250489)持有 <i1 = 5, id = 23> 的記錄的排他 Next-Key 鎖,等待獲得這條記錄的插入意向鎖。

*** TRANSACTION:
-- 事務 2
TRANSACTION 250490, ACTIVE 19 sec starting index read
...
DELETE FROM t_deadlock_1 WHERE `i1` = 5

*** HOLDS THE LOCK(S):
RECORD LOCKS space id 232 page no 5 n bits 72 \
  index idx_i1 of table `test`.`t_deadlock_1` trx id 250490 \
  lock_mode X waiting
...
*** WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 232 page no 5 n bits 72 \
  index idx_i1 of table `test`.`t_deadlock_1` trx id 250490 \
  lock_mode X waiting
...

上面也是從死鎖日志中摘出來的一小段,從這段日志可以看到,事務 2(250490)的 HOLDS THE LOCK(S) 和 WAITING FOR THIS LOCK TO BE GRANTED 的記錄都處于 waiting 狀態。

這是因為事務 2(250490)在等待獲得事務 1(250489)持有的 <i1 = 5, id = 23> 的記錄的排他 Next-Key 鎖,又阻塞了事務 1(250489)對 <i1 = 5, id = 23> 的記錄加插入意向鎖。

既然事務 1(250489)已經持有 <i1 = 5, id = 23> 的記錄的排他 Next-Key 鎖,也就是既鎖定了這條記錄,又鎖定了它前面的間隙。

理論上來說,事務 1(250489)再對這條記錄加插入意向鎖,可以直接獲得鎖。

為什么會被事務 2(250490)阻塞呢?

如果事務 1(250489)因為持有這條記錄的排他 Next-Key 鎖,就可以直接獲得這條記錄的插入意向鎖。

獲得插入意向鎖之后,插入 <i1 = 2, id = 25> 的記錄到 <i1 = 5, id = 23> 的記錄前面。

新插入的記錄,會導致事務 1 和事務 2 原來對 <i1 = 5, id = 23> 的記錄加的鎖都需要拆分。

已經獲得的鎖,拆分是沒有問題的。

事務 2(250490)在等待獲得 <i1 = 5, id = 23> 的記錄的排他 Next-Key 鎖,也會拆分,得到兩個處于等待狀態的鎖。

然而,InnoDB 卻不允許一個事務同時有兩個處于等待狀態的鎖。

基于這個規則,雖然事務 1(250489)已經持有 <i1 = 5, id = 23> 的記錄的排他 Next-Key 鎖,但是因為事務 2(250490)在等待獲得這條記錄的排他 Next-Key 鎖,事務 1(250489)想要對這條記錄加插入意向鎖,也需要等待。

事務 1(250489)和事務 2(250490)相互等待,就形成了死鎖,過程如下:

  • 事務 1 持有鎖。
  • 事務 2 等待獲得事務 1 持有的鎖。
  • 事務 1 等待事務 2 獲得并釋放鎖之后,才能獲得插入意向鎖。

4. 總結

如果事務 1 已經對某條記錄加了排他 Next-Key 鎖:

  • 沒有其它事務在等待獲得這條記錄的鎖,事務 1 想要往這條記錄前面的間隙插入記錄,不需要等待獲得插入意向鎖,可以直接插入記錄。
  • 其它事務在等待獲得這條記錄的鎖,事務 1 想要往這條記錄前面的間隙插入記錄,需要等待其它事務獲得并釋放鎖之后,事務 1 才能獲得插入意向鎖,然后才能往這個間隙插入記錄。
責任編輯:武曉燕 來源: 愛可生開源社區
相關推薦

2023-07-03 08:15:46

MySQLInnoDB

2022-11-28 14:27:17

插入意向鎖age

2024-05-15 09:23:45

MySQL排他鎖共享鎖

2022-04-21 10:39:29

InnoDB意向鎖SQL

2024-08-28 08:50:11

MySQL核心模塊

2024-09-04 08:44:18

MySQL核心模塊

2024-04-03 08:20:53

MySQL核心模塊

2024-06-05 11:49:33

2024-05-15 09:05:42

MySQL核心模塊

2025-06-04 02:55:00

MySQL意向鎖記錄鎖

2024-08-07 14:58:00

MySQL釋放鎖核心模塊

2023-12-28 17:33:25

意向鎖MySQL開發者

2024-10-16 11:11:51

隔離InnoDB死鎖

2024-03-27 13:33:00

MySQLInnoDB事務

2021-03-26 10:40:16

MySQL鎖等待死鎖

2017-06-07 16:10:24

Mysql死鎖死鎖日志

2024-05-29 10:17:01

2022-07-20 08:06:57

MySQL表鎖Innodb

2025-02-26 08:26:38

2022-12-27 17:40:57

意向鎖MySQLInnoDB
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 免费影视在线观看 | 毛色毛片免费看 | 精品不卡 | 欧美黄在线观看 | 欧美成人h版在线观看 | 日本久久久久久久久 | 日韩欧美久久精品 | 亚洲日本视频 | 久久国品片| 国产成人精品午夜视频免费 | 成人性生交大片免费看中文带字幕 | 午夜成人在线视频 | 免费黄色片在线观看 | 久久久国产一区 | 午夜爽爽爽男女免费观看影院 | 美女久久久久久久 | 欧美日韩国产高清视频 | 日韩欧美在线一区 | 手机看黄av免费网址 | 激情久久久久 | 成人福利网站 | 久久久免费 | 国产一区二区在线视频 | 国产高清视频在线观看 | 精品在线 | 亚洲免费网 | 成人在线视频一区 | 91网站在线播放 | 亚洲一av | 99re国产| 国产免费观看久久黄av片涩av | 伊人春色成人 | 国产精品成人在线播放 | 99视频精品 | 精品国产色 | 91香蕉视频在线观看 | 97国产在线观看 | 在线中文字幕av | 爱爱视频网 | 九九色综合 | 一区二区三区电影网 |