這次更新,選擇組合查詢執行緒安全嗎?
我有一個叫做席位的表,它有這樣的模式
身份證,拍攝
對於每個使用者,我隨機取一個未使用的 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
這樣,您可以在事務未完成時對行執行排他鎖。其他會話將等到行鎖被釋放。