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
操作將資料塞入新的分區。