2010-11-05 104 views
6

我有一個表是由不同線程同時讀取的。忽略MySQL查詢中鎖定的行

每個線程必須選擇100行,每行執行一些任務(與數據庫無關),那麼他們必須從表中刪除選定的行。

SELECT id FROM table_name FOR UPDATE; 

我的問題是:我怎麼可以忽略(或跳過)先前使用MySQL中的select語句鎖定行

行使用此查詢選擇?

回答

15

我通常會創建一個PROCESS_ID列,它是默認NULL,然後讓每個線程使用的唯一標識符,以執行以下操作:

UPDATE table_name SET process_id = #{process.id} WHERE process_id IS NULL LIMIT 100; 

SELECT id FROM table_name WHERE process_id = #{process.id} FOR UPDATE; 

這確保每個線程選擇一組唯一的從表中的行。

希望這會有所幫助。

+1

+1 – 2010-11-06 07:52:38

+1

在這個例子中,如果腳本中的第一次更新後突然中止,你會如何「乾淨」的。數據庫從舊的進程?否則這些100行將永遠不會被照顧 – 2013-12-08 00:42:26

+1

好問題。我通常設置相同的列或'狀態「欄來表示成功完成(作爲交易的一部分)。然後,如果一個進程死亡,您可以將一個不存在的進程的任何process_id設置回NULL以供另一個進程啓動。我更喜歡cron作業來進行清理,但你也可以把它作爲開始一個新過程的一部分。 – 2013-12-11 22:20:36

0

即使它不是最好的解決方案,因爲我不知道忽略鎖定的行,我選擇一個隨機的並嘗試獲得一個鎖。

START TRANSACTION; 
SET @v1 =(SELECT myId FROM tests.table WHERE status is NULL LIMIT 1); 
SELECT * FROM tests.table WHERE [email protected] FOR UPDATE; #<- lock 

設置爲交易小超時,如果該行被鎖定的交易被中止,我嘗試另一個。如果我獲得鎖,我會處理它。如果(運氣不好)該行被鎖定,它會被處理並且在我超時之前釋放鎖,然後我選擇一個已經被「處理」的行!但是,我檢查了一個我的進程設置的字段(例如狀態):如果其他進程事務結束了,那麼該字段告訴我工作已經完成,並且我不再處理該行。

沒有事務的其他任何可能的解決方案(例如,如果該行沒有狀態等等,設置另一個字段)可以容易地提供競爭條件和錯過的進程(例如,一個線程突然死亡,分配的數據仍然被標記,而交易到期;參考意見here

希望它使用限制是一個好主意,幫助