Mysql

這次更新,選擇組合查詢執行緒安全嗎?

  • November 25, 2014

我有一個叫做席位的表,它有這樣的模式

身份證,拍攝

對於每個使用者,我隨機取一個未使用的 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 簽入,對於該使用者,選擇查詢返回與 user1 相同的行。所以我將結束兩次更新同一行。

這個查詢是否可能出現這種情況?

評估

您的查詢對 MySQL 查詢優化器有點危險。

我有一篇舊文章(MySQL 子查詢問題)。提出的問題涉及此查詢

DELETE FROM test WHERE id =
(SELECT id FROM (SELECT * FROM test) temp ORDER BY RAND() LIMIT 1);

儘管 MySQL 查詢解析器將使用此查詢並接受它的語法,但 MySQL 文件暗示某些數據可能會在某些優化階段消失。在您的情況下,您只訪問一行以獲取 id,然後更新該行。儘管如此,從可能不會返回的子查詢中訪問數據的遠端可能性,id因為它可能會間歇性地消失,這對我來說並不是事務安全的,更不用說執行緒安全了。

您的查詢與該舊文章中的查詢相似

  • 非 SELECT 查詢
  • 選擇子查詢
  • 子查詢使用 LIMIT

這個問題超出了執行緒安全與否的限制。即使是這樣,您也不應該相信 MySQL 對在子查詢中間搶占可用席位的解釋。您需要您的查詢是交易安全的。

建議 #1

我對您的建議是將其重寫為事務中的三個查詢。

也許是這樣的:

START TRANSACTION;
SELECT id from seats WHERE taken is null limit 1 FOR UPDATE;
SELECT id INTO @available_id from seats WHERE taken is null limit 1;
UPDATE seats SET taken = 1 WHERE id = @available_id;
COMMIT;

或有兩個查詢

START TRANSACTION;
SELECT id INTO @available_id from seats WHERE taken is null limit 1 FOR UPDATE;
UPDATE seats SET taken = 1 WHERE id = @available_id;
COMMIT;

有關更多資訊,請閱讀MySQL 文件SELECT ... FOR UPDATE

建議 #2

我還將確保該seats表在所採用的列上有一個索引。

如果沒有,請添加此索引

ALTER TABLE seats ADD INDEX (taken);

在開發/暫存環境中執行此操作並在生產中執行此操作之前測試您的查詢。

試一試 !!!

如果你使用 InnoDB,你可以用SELECT ... FOR UPDATE句子來保護你的交易

http://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html

這樣,您可以在事務未完成時對行執行排他鎖。其他會話將等到行鎖被釋放。

引用自:https://dba.stackexchange.com/questions/83229