MySQL 事務的四個隔離級別
原子性( atomicity ) 一個事務必須被視為一個不可分割的最小工作單元,整個事務中的所有操作要么全部提交成功,要么全部失敗回滾,對於一個事務來說,不可能只執行其中的一部分操作,這就是辜務的原子性。
一致性( consistency ) 數據庫總是從一個一致性的狀態轉換到另外一個一致性的狀態。
在前面的例子中,一致性確保了,即使在執行第三、四條語句之間時系統崩潰,支票賬戶中也不會損失 200 美元,因為事務最終沒有提交,所以事務中所做的修改也不會保存到數據庫中。
隔離性( isolation ) 通常來說,一個事務所做的修改在最終提交以前,對其他事務是不可見的。
在前面的例子中,當執行完第三條語句、第四條語句還未開始時,此時有另外一個賬戶匯總程序開始運行,則其看到的支票賬戶的余額並沒有被減去 200 美元。
后面我們討論隔離級別( Isolation level )的時候,會發現為什么我們要說“通常來說”是不可見的。
持久性( durability ) 一旦事務提交,則其所做的修改就會永久保存到數據庫中。此時即使系統崩潰,修改的數據也不會丟失。
持久性是個有點模糊的概念,因為實際上持久性也分很多不同的級別。有些持久性策略能夠提供非常強的安全保障,而有些則未必。
而且不可能有能做到 100 %的持久性保證的策略(如果數據庫本身就能做到真正的持久性,那么備份又怎么能增加持久性呢?)。
在后面的一些章節中,我們會繼續討論 MySQL 中持久性的真正含義。事務的 ACID 特性可以確保銀行不會弄丟你的錢。而在應用邏輯中,要實現這一點非常難,甚至可以說是不可能完成的任務。
一個兼容 ACID 的數據庫系統,需要做很多復雜但可能用戶並沒有覺察到的工作,才能確保 ACID 的實現。
就像鎖粒度的升級會增加系統開銷一樣,這種事務處理過程中額外的安全性,也會需要數據庫系統做更多的額外工作。
一個實現了 ACID 的數據庫,相比沒有實現 ACID 的數據庫,通常會需要更強的 CPU 處理能力、更大的內存和更多的磁盤空間。
正如本章不斷重復的,這也正是 MysQL 的存儲引攀架構可以發揮優勢的地方。用戶可以根據業務是否需要事務處理,來選擇合適的存儲引笨。
對於一些不需要事務的查詢類應用,選擇一個非事務型的存儲引攀,可以獲得更高的性能。即使存儲引攀不支持事務,也可以通過 LOCK TABLES 語句為應用提供一定程度的保護,這些選擇用戶都可以自主決定。
下面簡單地介紹一下四種隔離級別。
READ UNCOMMITTED (未提交讀)
在READ UNCOMMITTED級別,事務中的修改,即使沒有提交,對其他事務也都是可見的。事務可以讀取未提交的數據,這也被稱為臟讀( Dirty Read )。
這個級別會導致很多問題,從性能上來說,READ UNCOMMITTED不會比其他的級別好太多,
但卻缺乏其他級別的很多好處,除非真的有非常必要的理由,在實際應用中一般很少使用。
READ COMMITTED(提交讀)
大多數數據庫系統的默認隔離級別都是 READ COMMITTED(但 MysQL 不是)。
READ COMMITTED 滿足前面提到的隔離性的簡單定義:一個事務開始時,只能“看見”已經提交的事務所做的修改。
換句話說,
一個事務從開始直到提交之前,所做的任何修改對其他事務都是不可見的。
這個級別有時候也叫做不可重復讀(nonrepeatable read) ,因為兩次執行同樣的查詢,可能會得到不一樣的結果。
REPEATABLE READ(可重復讀)
REPEATABLE READ 解決了臟讀的問題。該級別保證了在同一個事務中多次讀取同樣記錄的結果是一致的。
但是理論上,可重復讀隔離級別還是無法解決另外一個幻讀 ( Phantom Read )的問題。
所謂幻讀,指的是當某個事務在讀取某個范圍內的記錄時,另外一個事務又在該范圍內插入了新的記錄,
當之前的事務再次讀取該范圍的記錄時,會產生幻行( Phantom Row )。
InnoDB 和 XtraDB 存儲引攀通過多版本並發控制( Mvcc , Multiversion Concurrency Control )解決了幻讀的問題。
本章稍后會做進一步的討論。
可重復讀是 MySQL 的默認事務隔離級別。
SERIALIZABLE (可串行化)
SERIALIZABLE 是最高的隔離級別。它通過強制事務串行執行,避免了前面說的幻讀的問題。
簡單來說, SERIALIZABLE 會在讀取的每一行數據上都加鎖,所以可能導致大量的超時和鎖爭用的問題。
實際應用中也很少用到這個隔離級別.只有在非常需要確保數據的一致性而且可以接受沒有並發的情況下,才考慮采用該級別。