2017-07-06 103 views
7

插入意向鎖我有很簡單的表:解決方案在MySQL

CREATE TABLE `d` (
    `id` int(11) DEFAULT NULL, 
    UNIQUE KEY `id` (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

無記錄:

select * from d; 
Empty set (0,01 sec) 

然後我嘗試在不同的會議開兩筆交易:

會議#1:

begin; 
Query OK, 0 rows affected (0,00 sec) 

select * from d where id = 100 for update; 
Empty set (0,00 sec) 

會議#2:

begin; 
Query OK, 0 rows affected (0,00 sec) 

select * from d where id = 700 for update; 
Empty set (0,00 sec) 

現在,我嘗試在活動#2和會話 「凍結」 插入新記錄:

insert into d values (700); 

,當我嘗試做同樣的(與另一個id字段)在會議#1它崩潰:

insert into d values (100); --> ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction in Session #1 
insert into d values (700); --> Query OK, 1 row affected (4,08 sec) in Session #2 

我該如何解決僵局? InnoDB的狀態是:

------------------------ 
LATEST DETECTED DEADLOCK 
------------------------ 
2017-07-06 15:59:25 0x70000350d000 
*** (1) TRANSACTION: 
TRANSACTION 43567, ACTIVE 15 sec inserting 
mysql tables in use 1, locked 1 
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1 
MySQL thread id 4, OS thread handle 123145358217216, query id 89 localhost root update 
insert into d values (700) 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43567 lock_mode X insert intention waiting 
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 
0: len 8; hex 73757072656d756d; asc supremum;; 

*** (2) TRANSACTION: 
TRANSACTION 43568, ACTIVE 7 sec inserting 
mysql tables in use 1, locked 1 
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1 
MySQL thread id 3, OS thread handle 123145357938688, query id 90 localhost root update 
insert into d values (100) 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43568 lock_mode X 
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 
0: len 8; hex 73757072656d756d; asc supremum;; 

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43568 lock_mode X insert intention waiting 
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 
0: len 8; hex 73757072656d756d; asc supremum;; 

*** WE ROLL BACK TRANSACTION (2) 
+0

你想解決什麼問題:)?這是想知道爲什麼MySQL正在做它在做什麼?或者是否存在您正試圖解決的特定問題? – dm03514

+0

我嘗試解決我的應用程序中的死鎖(當然表d不是真實的)。爲了更加清晰,增加了這個問題。 –

回答

1

我懷疑發生了死鎖,因爲InnoDB在處理「空白」方面保守。請注意,100和700都在原始土地的相同雲霧區域。 InnoDB不能(或者至少不會)處理「100」和「700」不同的事實。 InnoDB想要標記單個行,但表中沒有已經存在的行。

交易2可能會超時(請參閱innodb_lock_wait_timeout)。當您第二次觸發交易1並且#2仍然想要鎖定時,InnoDB會放棄並放棄。

底線:存在死鎖。當它們發生時,請回到BEGIN。你已經發現了另一個不必要的死鎖發生的晦澀事件。此外,我懷疑修復這種情況下的代碼會減慢大多數其他情況,並導致一系列錯誤,這些錯誤需要幾個版本才能發現和修復。

0

注意,開始使用MySQL 8.0.1, 性能模式暴露出InnoDB的數據鎖。

https://dev.mysql.com/doc/refman/8.0/en/data-locks-table.html

https://dev.mysql.com/doc/refman/8.0/en/data-lock-waits-table.html

在這個例子中,後在單獨的會話1的第一選擇鎖是:

mysql> select * from performance_schema.data_locks \G 
*************************** 1. row ***************************                            
       ENGINE: INNODB                                    
     ENGINE_LOCK_ID: 1808:76                                    
ENGINE_TRANSACTION_ID: 1808                                     
      THREAD_ID: 35                                     
      EVENT_ID: 13081                                     
     OBJECT_SCHEMA: test                                     
      OBJECT_NAME: d                                      
     PARTITION_NAME: NULL                                     
    SUBPARTITION_NAME: NULL 
      INDEX_NAME: NULL 
OBJECT_INSTANCE_BEGIN: 139756088373592 
      LOCK_TYPE: TABLE 
      LOCK_MODE: IX 
      LOCK_STATUS: GRANTED 
      LOCK_DATA: NULL 
*************************** 2. row *************************** 
       ENGINE: INNODB 
     ENGINE_LOCK_ID: 1808:2:5:1 
ENGINE_TRANSACTION_ID: 1808 
      THREAD_ID: 35 
      EVENT_ID: 13111 
     OBJECT_SCHEMA: test 
      OBJECT_NAME: d 
     PARTITION_NAME: NULL 
    SUBPARTITION_NAME: NULL 
      INDEX_NAME: id 
OBJECT_INSTANCE_BEGIN: 139756088370552 
      LOCK_TYPE: RECORD 
      LOCK_MODE: X 
      LOCK_STATUS: GRANTED 
      LOCK_DATA: supremum pseudo-record <--- HERE 
2 rows in set (0.00 sec) 

這不是僵局自身的解決方案,但瞭解所發現的鎖具對理解問題有很大的幫助。

這裏,SELECT FOR UPDATE鎖定「suprenum」記錄,因爲id 100和700大於表中最大的ID(它是空的)。

再次出現記錄(比如ID = 500),兩個查詢應該同時執行,因爲ID中的不同間隙將被鎖定。

+0

謝謝。但是現在我在MySQL 5.7上測試了它 –