2010-03-18 104 views
10

僵局,我有以下查詢(所有表都InnoDB的)如何避免在MySQL

INSERT INTO busy_machines(machine) 
       SELECT machine FROM all_machines 
       WHERE machine NOT IN (SELECT machine FROM busy_machines) 
       and machine_name!='Main' 
       LIMIT 1 

從而導致死鎖,當我在線程中運行它,內部的選擇顯然是因爲,對不對?

我得到的錯誤是:

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

我怎樣才能避免僵局?有沒有辦法改變查詢來使它工作,還是我需要做別的事情?

錯誤不會總是發生,當然,只有在多次運行此查詢並在多個線程中出現錯誤。

+0

您是否遇到死鎖或鎖爭用? – Quassnoi 2010-03-18 13:47:54

+0

@Quassnoi:我在問題中添加了信息 - (1213,'嘗試鎖定時發現死鎖;嘗試重新啓動事務') – olamundo 2010-03-18 14:09:33

+0

'show innodb status'會簡要描述上一個死鎖的原因 – jonny 2010-03-18 15:37:15

回答

5

如果用外連接替換「NOT IN」,您可能會獲得更好的性能。

您也可以將其分成兩個查詢,以避免在單個查詢中插入和選擇相同的表。

事情是這樣的:

  SELECT a.machine 
      into @machine 
      FROM all_machines a 
      LEFT OUTER JOIN busy_machines b on b.machine = a.machine 
      WHERE a.machine_name!='Main' 
      and b.machine IS NULL 
      LIMIT 1; 

      INSERT INTO busy_machines(machine) 
      VALUES (@machine); 
+0

如果使用MySQL 5.0或更高版本,這是一個很好的解決方案,否則您將不會有用戶變量可用。這應該避免你的死鎖問題。 – 2010-03-18 14:40:18

+0

我不明白你的查詢與我的相似如何 - 外連接還會返回處於繁忙機器中的機器,而不僅僅是那些不是 – olamundo 2010-03-18 15:18:17

+0

@noam的機器,使用外連接與「and b.machine IS NULL「在where子句中排除busy_machines中的機器。它將返回與NOT IN相同的數據,但效率更高。 – 2010-03-18 16:10:05

11

據我的理解,一個選擇不獲取鎖定,不應該是死鎖的原因。

每次插入/更新/或刪除一行時,都會獲取鎖定。爲避免死鎖,您必須確保併發事務不會以可能導致死鎖的順序更新行。一般來說,爲了避免死鎖即使在不同的交易中(例如總是首先是表A,然後是表B),您也必須始終以相同順序獲得鎖

但是,如果在一次事務中只插入一個表中,則會滿足此條件,而這通常不會導致死鎖。你在交易中做了其他事情嗎?

但是,如果存在缺失索引,會發生死鎖。當插入/更新/刪除一行時,數據庫需要檢查關係約束,也就是確保關係一致。爲此,數據庫需要檢查相關表中的外鍵。它可能導致其他鎖獲取比被修改的行。請確保始終在外鍵(當然還有主鍵)上有索引,否則可能會導致表鎖而不是行鎖。如果表鎖定發生,則鎖定爭用更高並且死鎖的可能性增加。

不知道你的情況究竟發生了什麼,但也許它有幫助。

+0

-1; MySQL甚至不會讓你在兩列之間創建一個外鍵,除非兩者都被索引,所以外鍵上缺少的索引不可能解釋任何東西。 – 2016-11-10 14:36:38