2014-03-31 84 views
0

我想用MYSQL實現一個隊列,並且想確保我正確理解SELECT FOR UPDATESELECT FOR UPDATE如何實際工作?

我的表:

Table jobs 
Fields: id (INT), state (VARCHAR), queued_time (TIMESTAMP) 

當我插入一個作業,狀態QUEUED。當我鎖定作業時,狀態變爲PROCESSING

我有多臺機器,每臺機器都使用相同的數據庫連接。當一臺機器準備搶東西從隊列中,它調用

SELECT FROM jobs WHERE state = "QUEUED" ORDER BY queued_time ASC LIMIT 1 FOR UPDATE; UPDATE jobs SET state = "PROCESSING" WHERE state = "QUEUED" ORDER BY queued_time ASC LIMIT 1; 

查詢後,我檢查UPDATE成功,如果它這樣做,我讓機器來處理由SELECT FOR UPDATE返回的工作。

假設機器1和2已準備好從隊列中取出某些東西。隊列是這樣的:

id  state  queued_time 
1   QUEUED  2014-03-30 20:04:43 
2   QUEUED  2014-03-30 22:04:43 

機1將在時間t2執行SELECT FOR UPDATE在時間t1UPDATE。當機器2執行SELECT FOR UPDATEUPDATE時,會發生什麼情況?t1t2?哪些發生?

- Machine 1 ends up with job 1, and machine 2's `UPDATE` fails because machine 1 locked the row and never unlocked it (this is my current understanding) 

- Machine 2 ends up with job 1, and machine 1's `UPDATE` fails 

- Both machines end up with job 1 

當機2執行t2t1t2UPDATE之間SELECT FOR UPDATE會發生什麼?哪些發生?

- Machine 1 ends up with job 1, and machine 2's `UPDATE` fails 

- Machine 2 ends up with job 1, and machine 1's `UPDATE` fails 

- Both machines end up with job 1 because machine 2's `UPDATE` succeeds since machine 1 release the lock (this is my current understanding) 

回答

1

您需要執行事務(autocommit=0),因爲只有SELECT FOR UPDATE在事務完成之前持有該鎖的內部這些行動。

您的場景都不會發生。交易不會立即失敗,但可能會超時。

如果事務2嘗試鎖定與事務1相同的記錄(鎖定發生在SELECT FOR UPDATE中),則事務2必須等到事務1完成。如果達到鎖定超時時間(innodb_lock_wait_timeout),則事務將超時(「失敗」)。

確保您在任何時候選擇記錄並服從您使用的任何當前鎖定SELECT FOR UPDATE。沒有FOR UPDATE的正常SELECT不會等待任何鎖定。

顯然,您應該儘可能快地完成任何交易。

你的方法應該可以正常工作。您應該考慮檢索SELECT FOR UPDATE語句中的主鍵值,然後僅通過UPDATE語句中的主鍵值引用記錄。