MySQL是廣泛使用的關系型數據庫系統之一,除了 ACID 屬性外,還有許多特殊的事務隔離級別,其中包括不可重復讀、幻讀等。本文將會討論這兩種現象,并且提供一些解決方法。
不可重復讀
不可重復讀是指,在一個事務的兩個不同時間點,執行相同的 SELECT 語句卻得到了不同的結果。這是因為在過程中某個事務修改了數據的某部分,從而影響了另一個事務的查詢結果。
-- 示例代碼 -- 事務1 START TRANSACTION; SELECT * FROM users WHERE username = 'Alice'; -- 返回 1 條記錄 -- 另一個事務執行了更新 -- 事務1 執行相同的 SELECT 語句,返回 0 條記錄 COMMIT;
不可重復讀問題可以通過使用SELECT ... FOR UPDATE
或SELECT ... LOCK IN SHARE MODE
來解決。這樣,讀取操作將獲取到行級鎖,其它操作在等待鎖被釋放后才會執行。
幻讀
幻讀是指,在一個事務的兩個不同時間點,執行相同的 SELECT 語句卻得到了不同的結果。它與不可重復讀類似,不過幻讀主要發生在插入或刪除操作時。當某個事務插入或刪除一行數據時,另一個事務如果執行 SELECT 時,可能發現了新增或者刪除操作的影響,因此得到了不同的結果。
-- 示例代碼 -- 事務1 START TRANSACTION; SELECT COUNT(*) FROM users; -- 返回 10 -- 事務2 刪除了1行記錄 -- 事務1 執行相同的 SELECT 語句,返回 9 條記錄 COMMIT;
跟不可重復讀一樣,幻讀問題也可以通過行級鎖以及使用更高的隔離級別來解決。使用串行化隔離級別是最安全的選擇,但也會造成嚴重的性能影響。更好的方式是優化事務的操作順序,如使用更加有效的 SELECT 語句來將影響最小化。