type
status
date
slug
summary
tags
category
icon
password

重現

事前準備 (schema、初始資料)
模擬並行 update 資料的程式
測試結果如下表:
語法:UPDATE round_to_txn SET end_time = "2022-11-16 08:53:08” WHERE …
是否有 deadlock
round_id = ?
Yes
round_id = ? AND end_time = ‘0000-00-00 00:00:00’
No
round_id = ? AND end_time <= '2022-10-31 23:59:59'
No
round_id = ? AND end_time <= '2022-11-10 23:59:59'
No
round_id = ? AND (end_time <= '2022-10-31 23:59:59' OR end_time >= '2022-12-01 00:00:00' )
Yes

DeadLock 分析

看一下上方的 log 可以看到 deadlock 的原因是兩者都有 GAP LOCK,而 GAP LOCK 本身互相不衝突,但是都會阻塞 INSERT 操作來達到 RR 級別的可重複讀,就在這時候 2 個 Transaction 同時又要求 Insert Intention Locks,這就導致雙方都在等待對方釋放 GAP LOCK,因此發生了 DEADLOCK。
但是問題來了,這兩個 Transaction 都是 UPDATE 操作,為什麼會有 INSERT 操作才有的 Insert Intention Locks 呢?

實驗

沒有 partition 的測試

我們建立一個 schema 相同,但是沒有 partition 的 table 測試

單獨一個 update 操作的 Lock 測試

可以看到如果在沒有 partition 的 table 上進行 update 操作會有以下 LOCK:
  • 該 Table 的 IX
  • 因為是透過 PK 來掃描,因此在 PK 上命中的行加上 Record Lock
  • 因為條件沒有覆蓋 PK 條件,因此在符合的位置加上 Gap Lock 避免被 INSERT

複數 update 操作的 Lock 測試

可以看到當有 2 句 update 操作時其實 LOCK 情況並沒有分別,且因為兩者 Record Lock 不衝突,且 Gap Lock 互相不阻塞,因此兩者都能成功 UPDATE。

有 partition 的測試

單獨一個 update 非 partition key操作的 Lock 測試

看起來和 partition 的單獨 update 相同,只是因為有切 partition,所以需要對每個 Partition 加上 IX 。

複數 update 非 partition key 操作的 Lock 測試

可以看到和單獨執行一句 update 一樣,只有 Record Lock、Gap Lock 並沒有產生衝突

單獨一個 update partition key 操作的 Lock 測試

看起來和 partition 的單獨 update 相同,只是因為有切 partition,所以需要對每個 Partition 加上 IX 。

複數 update partition key 操作的 Lock 測試

我們可以看到出現了 Insert Intention Locks,因為 Insert Intention Locks 是延遲加鎖機制,只有和 Gap Lock 發生衝突才加上來的,所以單獨執行沒發生衝突也看不到 Insert Intention Locks,同時我們也知道了當在

複數 update partition key 操作,但不變動 partition key 的 Lock 測試

我們可以看到這一次沒有 Insert Intention Locks,這是因為 update 的值沒有導致資料需要被變更到別的 partition,所以加鎖的情況會等同於普通的 update 並不會產生 Insert Intention Locks 從而導致 DeadLock 的發生。

結論

在有 partition 的 Table 上執行 UPDATE 操作更新 partition key 發生跨區時,會先由 UPDATE 操作產生 Record Lock (或 Next Key Lock 也就是包含 Gap Loc),之後轉變成 INSERT 操作將資料塞入新的分區。
notion image