2014-11-20 70 views
4

我有一個名爲座位表,其中有這樣的這次更新,選擇組合查詢線程安全嗎?

ID的模式,採取

對於每一個用戶,我採取隨機尚餘ID和分配給該用戶,這裏簡單說,我讓= 1。我使用

update seats u inner join (
     SELECT id from seats 
     where taken is null limit 1) s 
    on s.id = u.id set taken = 1; 

此查詢的查詢需要與拍攝標誌空隨機座位,併爲座位它使標誌1.在此查詢工作正常,這是線程安全的?

考慮這種情況,我有兩個用戶並行。對於user1,我選擇X行,並且在更新查詢運行之前,user2簽入,並且對於該用戶,select查詢返回與user1相同的行。所以我會結束更新同一行兩次。

此查詢可以使用此方案嗎?

+0

你正在使用哪種編程語言與MySQL? – 2014-11-24 07:39:58

+0

@FathahRehmanP我正在使用PHP。但是,問題語言不是獨立的。 – Dude 2014-11-24 08:36:15

+0

你可以嘗試像SELECT ... FOR UPDATE – 2014-11-24 09:39:32

回答

2

與MySQL,有一個簡單的,幾乎令人難以置信,解決方法:

update seats set taken = 1 
where taken is null 
limit 1 

這句法限制影響的更新過程爲1的行數,使用MySQL的特殊擴展到更新語法實現自己的意圖。

當然,作爲官方支持的擴展,這是一個原子動作並且完全線程安全。

雖然既不是你的代碼也不是你的問題建議能夠捕捉 ID是更新,您可以通過用戶定義的變量捕捉它:

update seats set 
taken = 1, 
id = (@id := id) 
where taken is null 
limit 1; 

select @id id; 

從選擇要麼標識返回的值如果沒有更新行,則更新或爲null。

+0

我同意,這是更簡單和直觀。但是,僅僅是爲了我對數據庫的理解,我也是在原子問題中發佈的查詢呢? – Dude 2014-11-24 09:58:44

+0

另外,在您的方法中,我將如何知道哪個seat_id已被拍攝? – Dude 2014-11-24 10:14:48

1

只要記錄在案,您可以在任何RDBMS中運行的所有SQL命令都是線程安全的,只要它是我們正在討論的服務器,並且每個線程都使用自己的連接。實際上,數據庫服務器正是爲此目的而創建的。

兩個或多個線程/進程併發訪問數據庫服務器時可能會遇到的唯一問題是死鎖。這是您可以在程序邏輯中防止的問題。當一個連接持有一個資源(一個數據庫的記錄或表)並要求另一個連接時,死鎖會發生,而其他連接將以相反的順序獲取相同的兩個資源。這兩個連接將等待另一個連接釋放它們的資源,這在理論上可能會延續下去。 MySQL通過在有限的時間內授予鎖來解決這個問題。

如果要防止死鎖,最簡單的解決方案是考慮鎖定訂單。這樣兩個連接就不可能面對面。

回到你的SQL語句,我的答案和Bohemian的完全一樣。如果硬要它是隨機的,你還可以:

UPDATE seats 
SET 
    taken = 1 
WHERE 
    taken IS NULL 
ORDER BY RAND() 
LIMIT 1 

這只是你需要知道WHERE taken IS NULL是一個非常糟糕的事情。如果你想搜索一個字段,它應該始終是NOT NULL,特別是如果涉及到鎖定的話。也許你可以指定0而不是NULL,它會提升很多的性能!