簡體   English   中英

了解 InnoDB 死鎖日志

[英]Understanding InnoDB deadlock log

我有一個正在執行多個INSERT查詢的事務。 同時,可能會發生一個作業,該作業正在對另一個用戶運行的數據庫執行一致性檢查,該作業通過對數據庫發出SELECT查詢來進行復制校驗和計算。

問題是有時我的應用程序級事務與執行這些檢查的工具中的事務發生死鎖:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2022-05-19 18:25:20 0x7f6eb63e3700
*** (1) TRANSACTION:
TRANSACTION 421588457956552, ACTIVE 0 sec fetching rows
mysql tables in use 2, locked 2
LOCK WAIT 1012 lock struct(s), heap size 123088, 85725 row lock(s)
MySQL thread id 15333390, OS thread handle 140086616594176, query id 2884722462 10.96.7.108 replication_checksum_user Sending data
REPLACE INTO `percona`.`checksums` (db, tbl, chunk, chunk_index, lower_boundary, upper_boundary, this_cnt, this_crc) SELECT 'bar', 'bars', '17', 'foo+idx', '688438b1-63b7-4cdd-ba14-5ac2811fce08,688438b1-63b7-4cdd-ba14-5ac2811fce08,6149061644177471', '6f2aecfe-44b8-4b04-8913-5086c5402c02,6f2aecfe-44b8-4b04-8913-5086c5402c02,4356115808993199', COUNT(*) AS cnt, COALESCE(LOWER(CONCAT(LPAD(CONV(BIT_XOR(CAST(CONV(SUBSTRING(CRC32(CONCAT_WS('#', convert(`field1` using utf8mb4), convert(`field2` using utf8mb4), convert(`field3` using utf8mb4), convert(`field4` using utf8mb4), `field5`, `field6`, `created_time`, `finished_time`, CONCAT(ISNULL(`notification_id`), ISNULL(`attempt_type`), ISNULL(`successful`), ISNULL(`finished_time`)))), 1, 16), 16, 10) AS UNSIGNED)), 10, 16), 16, '0')))
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 109 page no 16087 n bits 192 index foo_idx of table `bar`.`bars` trx id 421588457956552 lock mode S waiting
Record lock, heap no 120 PHYSICAL RECORD: n_fields 10; compact format; info bits 0
 0: len 30; hex 36626361393436342d663735642d343134312d623231652d306130623961; asc 6bca9464-f75d-4141-b21e-0a0b9a; (total 36 bytes);
 1: len 16; hex 34373236323438353934373634393231; asc 4726248594764921;;
 2: len 6; hex 000028626bdf; asc   (bk ;;
 3: len 7; hex ba0004c0290d2e; asc     ) .;;
 4: SQL NULL;
 5: SQL NULL;
 6: SQL NULL;
 7: len 8; hex 8000000000000a07; asc         ;;
 8: len 5; hex 99ace72654; asc    &T;;
 9: SQL NULL;

*** (2) TRANSACTION:
TRANSACTION 677538783, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 3
MySQL thread id 15333007, OS thread handle 140113480660736, query id 2884722486 10.96.7.206 app_user Update
INSERT into bars(field1, field2, field3, created_time) VALUES('6bca9464-f75d-4141-b21e-0a0b9a7d06ca', '1656221208545861', 272.97000, CURRENT_TIMESTAMP())
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 109 page no 16087 n bits 192 index foo_idx of table `bar`.`bars` trx id 677538783 lock_mode X locks rec but not gap
Record lock, heap no 120 PHYSICAL RECORD: n_fields 10; compact format; info bits 0
 0: len 30; hex 36626361393436342d663735642d343134312d623231652d306130623961; asc 6bca9464-f75d-4141-b21e-0a0b9a; (total 36 bytes);
 1: len 16; hex 34373236323438353934373634393231; asc 4726248594764921;;
 2: len 6; hex 000028626bdf; asc   (bk ;;
 3: len 7; hex ba0004c0290d2e; asc     ) .;;
 4: SQL NULL;
 5: SQL NULL;
 6: SQL NULL;
 7: len 8; hex 8000000000000a07; asc         ;;
 8: len 5; hex 99ace72654; asc    &T;;
 9: SQL NULL;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 109 page no 16087 n bits 192 index foo_idx of table `bar`.`bars` trx id 677538783 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 120 PHYSICAL RECORD: n_fields 10; compact format; info bits 0
 0: len 30; hex 36626361393436342d663735642d343134312d623231652d306130623961; asc 6bca9464-f75d-4141-b21e-0a0b9a; (total 36 bytes);
 1: len 16; hex 34373236323438353934373634393231; asc 4726248594764921;;
 2: len 6; hex 000028626bdf; asc   (bk ;;
 3: len 7; hex ba0004c0290d2e; asc     ) .;;
 4: SQL NULL;
 5: SQL NULL;
 6: SQL NULL;
 7: len 8; hex 8000000000000a07; asc         ;;
 8: len 5; hex 99ace72654; asc    &T;;
 9: SQL NULL;

*** WE ROLL BACK TRANSACTION (2)

我在理解該日志時遇到了困難。 兩個事務爭奪的共同資源是什么? 是否是正在插入的特定行的索引記錄(id = 6bca9464-f75d-4141-b21e-0a0b9a7d06ca)?

根據我的理解和閱讀,第一個事務試圖獲取 S 鎖(用於讀取),第二個事務為插入持有 X 鎖(排他鎖)。 但是,第二筆交易在等待第一筆交易持有的是什么? 對我來說,第二個事務似乎正在等待它自己持有的鎖 - 這是錯誤的嗎?

另外,這兩個事務是否有可能正在等待他們不持有的鎖?

有間隙和無間隙部分是什么意思?

解決此類問題的首選方法是什么?

編輯:這是架構

CREATE TABLE `bars` (
    `field1` VARCHAR(50) NOT NULL,
    `field2` VARCHAR(50) NOT NULL,
    `field3` VARCHAR(50) NULL DEFAULT NULL,
    `field4` ENUM('WHOLE','PARTIAL') NOT NULL,
    `field5` TINYINT(4) NULL DEFAULT NULL,
    `field6` DECIMAL(16,2) NOT NULL,
    `created_time` DATETIME NOT NULL,
    `finished_time` DATETIME NULL DEFAULT NULL,
    UNIQUE INDEX `foo_idx` (`field1`, `field2`),
    CONSTRAINT `f1_idx` FOREIGN KEY (`field1`) REFERENCES `bays` (`field1`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

然后是兩次遷移

ALTER TABLE `bars`
ADD FOREIGN KEY `ff_fk` (`field1`, `field2`)
REFERENCES `fields_references` (`field1`, `field2`);
ALTER TABLE bars
DROP CONSTRAINT ff_fk;

由 pt-table-checksum 運行的 REPLACE 和您的應用程序運行的 INSERT 之間存在爭用。

您詢問了有關鎖定 SELECT 的問題。 一個普通的 SELECT 默認是無鎖的,但是如果一個 SELECT 是一個鎖定語句的一部分,它就會隱式地變成一個鎖定 SELECT。 也就是說,因為 pt-table-checksum 運行REPLACE INTO checksums ... SELECT FROM bars ,它必須在bars中的某些行上獲取 S 鎖。

在將數據寫入行或變量的語句中使用的任何 SELECT 也是如此。

  • 創建表...選擇...
  • 插入...選擇...
  • SET @variable = (SELECT ...)
  • 選擇 ... INTO @變量
  • 在 UPDATE 或 DELETE 語句的子查詢中使用 SELECT
  • 在觸發器中使用 SELECT

將應用的事務隔離級別更改為 READ-COMMITTED 有助於避免間隙鎖。 但也許 pt-table-checksum 有自己的事務隔離級別,無論如何都會創建間隙鎖。

底線是發生了死鎖。 您不能全部消除它們,它們是由並發會話完成鎖定時的自然結果。 因此,您應該設計代碼以捕獲異常並根據需要重試。


回復您的評論:

事務 1 不一定持有另一個鎖。 以下操作序列可能會導致死鎖:

  1. 事務 2(您的應用程序)在bars的單行上獲得了一些鎖。

  2. 事務 1 (pt-table-checksum) 需要鎖定一批多行。 至少有一個已被事務 2 鎖定,因此 pt-table-checksum 等待。

  3. 事務 2 想要鎖定它之前沒有鎖定的另一行,但該行是 pt-table-checksum 嘗試鎖定的批處理的一部分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM