事務的四大ACID 屬性
Atomicity 原子性、Consistency 一致性、Isolation 隔離性、Durability 持久性。
原子性: 事務是最小的執行單位不可分割,強調事務的不可分割
一致性: 事務執行前后,數據的完整性保持一致。
隔離性: 一個事務執行的前后不應該受到其他事務的干擾。
持久性: 一旦事務執行結束,數據就保存到數據庫。
事務
在事務的概念產生之前的很長一段時間內.人們都無法利用到數據庫的更多先進待征。事務是一組原子性的SQL查詢語句,也可以被看做一個工作單元。如果數據庫引擎能夠成功地對數據庫應用所有的查詢語句,它就會執行所有查詢,但是,如果任何一條查詢語句因為崩潰或其他原因而無法執行,那么所有的語句就都不會執行。也就是說,事務內的語句要么全部執行,要么一句也不執行。
銀行應用是一個經典實例,可以解釋事務應用的必要性。假設:銀行數據庫有兩張表,checking 表和saving表。
現在要從Jane的支票賬戶(Cheking Accoumn)里轉賬200美元到她的存款賬戶 (Savings Account)。那么,需要至少完成3步操作:
1檢查支票存款賬戶的余額是否大於200$。2.從支票存款賬戶余額中減去200.
3.在存款賬戶余額中增加200$
所有的操作被打包在一個事務里,如果某一步失敗,就回滾所有已完成步驟。
可以用START TRANSACTION 語句開始一個事務,用COOMIT語句提交整個事務,永久地修改數據,或者用ROLLBACK語句回滾整個事務,取消已做的修改。事務SQL樣本如下:
- START TRANSACTION;
- SELECT balance FROM checking WHERE customer_id = 10233276;
- UPDATE checking SET balance=balanse - 200.00 WHERE customer_id = 10233276;
- UPDATE savinge SET balance = balaice + 200.00 WHERE customer_id = 10233276;
- COMMIT;
單純的事務概念不是全部的主題。試想一下,如果數據庫服務器在執行第4條語句時突然崩潰,會發生什么事? 沒人知道,但用戶可能會損失200美元。再假如,在執行第3和第4條語句之間時,另一個進程同時運行,它的目的是要先刪除支票存款賬戶的全部余額,那么結果可能是,銀行根本不知道有這個邏輯先發事件,白白給用戶增加了200元存款。
除非系統通過ACID測試,否則空談事務概念是不夠的。ACID代表了原子性(Atomicity)、一致性(Consistency)、隔離性(solation) 和特久性(Durability)。 這些概念與事務的處理標准密切關聯,一 個有效的事務處理系統必須滿足相關標准。
原子性(Atomicaty)
一個事務必須被視為一個單獨的內部“不可分”的工作單元,以確保整個事務要么全部執行,要么全部回滾。當一個事務具有原子性時,該事務絕對不會被部分執行,要么完全執行,要么根本不執行。一致性(Consistency)
數據庫總是從一種致性狀態轉換到另一種一致性狀態。 在上述例子中,一致性確保了,即使數據庫系統在執行第3、4條語句時崩潰了,支票存款賬戶也不會損失200美元。因為最終事務根本沒有被提交,任何事務處理過程中所做的數據改變,也不會影響到數據庫的內容。隔離性(lsolation)
某個事務的結果只有在完成之后才對其他事務可見。在上述例子中,當數據庫執行完第3條語句,還未執行第4條語句時,如果此時銀行匯總程序也同時運行,它將仍視轉賬的200美元仍在支票存款賬戶內。當后文討論隔離級時,讀者就會理解為什么我們所說的通常是“不可見”(Invisible) 的。持久性(Durability)
一旦一個事務提交,事務所做的數據改變將是永久的。這意味若數據改變已被記錄,即使系統崩潰,數據也不會因此丟失。持久性是個有點模糊的概念,因為實際上持久性也分很多級別。有些持久性策略提供一種強壯的安全保證,另一些則未必。 另外,也沒有什么東西是100%永遠持久的。在本章的后續章節,將會討論MySQL中持久性的真正含義,特別是在第283頁"InoDBIO調優”一節。ACID事務確保了銀行不會弄丟你的錢,而這種特性在應用邏輯設計中是很難實現的,甚至不可能實現。一個ACID兼容的數據庫服務器,要為事務處理做大量的復雜工作,確保ACID特性的實現,而這也許是用戶未能察覺的。
正像鎖粒度的增加會導致鎖開銷的增長一樣, 這種事務處理中的額外安全措施,也導致數據庫服務器要完成更多的額外工作。通常,一個支持ACID特性的數據庫,相對於不支持這種特性的數據庫,需要更強的CPU處理能力、更大的內存和更多的磁盤空間。正如本章不斷重復的,這正是選用MySQL存儲引擎架構的有利之處。用戶可以根據應用是否需要事務處理,選擇相應的存儲引擎。如果對於某些類型的數據查詢,用戶不需要真正的事務處理,他可以選擇一個非事務處理型的存儲引擎來實現查詢,以獲得更高的處理性能。用戶也可以使用LOCK TABLES 語句,為應用提供某種級別的數據保護,而這些選擇完全由用戶自主決定。——《高性能MySQL》
MySQL的事務隔離級別
有四種:未提交讀、已提交讀、可重復讀和串行化。
未提交讀: 事務A可以讀取到事務B還未提交的數據。比如事務B將小明的賬戶余額從100改到110,事務B還沒提交,在這個隔離級別下事務A就讀取到的數據就是110。如果說事務B回滾了,那數據庫里小明的賬戶余額又變成100了,可是事務A返回的數據卻是110。這就造成了臟讀。
已提交讀: 事務A讀取到數據是事務B提交后的數據,即一個事務提交后,其變更才會被另一個事務讀取到。比如事務B將小明的賬戶余額從100改到110,事務B還沒提交的時候,事務A讀取到的數據仍然是100。因為在這個隔離級別下一個事務只能讀取到另一個事務修改后且提交事務后的數據。但是這種情況會造成一個問題就是,事務A在事務B隱式提交數據后讀取到的數據是110,這是沒問題的,但是在事務A還沒提交前,事務B又將小明的賬戶余額改為120並隱式提交,然后事務A再來讀取的時候就讀到了120,所以事務A的兩次讀取結果不一致。這就造成了不可重復讀。
可重復讀: 事務A只能讀到事務B修改的已提交了事務的數據,但是第一次讀取的數據,即使別的事務修改了這個值,這個事務再讀取這條數據的時候還是和第一次獲取的一樣,不會隨着別的事務的修改而改變。這和已提交讀的區別就在於,它重復讀取的值是不變的。所以叫可重復讀,很貼切的名字。拿剛才的例子就是,事務A在自己的事務里多次讀取小明的賬戶余額時,如果第一次讀取的是100,那么不管在這之后事務B對它進行如何的修改,對於事務A來說多次讀取的結果都是100。可重復讀解決了不可重復讀(聽起來像是廢話),但是存在幻讀的情況。
幻讀:當事務A查詢賬戶余額=100的客戶個數時,假如返回的是1萬,這時事務B將小明的賬戶余額由110改為100並提交事務,事務A再次查詢賬戶余額=100的客戶個數時返回的統計個數變成了1萬零1。這就是幻讀。
幻讀和不可重復讀有區別。不可重復讀側重點在於讀取同一條數據,數據被修改,如讀小明的賬戶余額;幻讀的側重點在於新增或者刪除 (數據條數變化),同樣的條件兩次查詢記錄數不一樣。
串行化: 上面三個隔離級別對同一條記錄的讀和寫都可以並發進行,但是串行化的隔離級別下就只能進行讀-讀並發。只要有一個事務操作一條記錄的寫,那么其他事務要讀這條記錄的事務都得等着。例如,事務B修改小明的賬戶還未提交前事務A來讀,那么事務A就只能等着事務B提交之后才能讀。串行化的隔離級別一半沒人用,因為性能比較低,常用的是已提交讀和可重復讀。
隔離級
隔離的問題比想象的要復雜。SQL標誰定義了4類隔離級,包括了一些具體規則,用來限定事務內外的哪些改變是可見的,哪些是不可見的。低級別的隔離級一般支持更高的井發處理,並擁有更低的系統開銷。
提示:每種存儲引擎實現的隔離級略有不同,如果用過其他數據庫產品,用戶可能發現它們未必完全滿足自己的期望(因此,本文不會討論更多的詳盡細節)。讀者可以根很據選擇的存儲引擊, 進一步閱讀相關手冊資料。
下面簡單介紹四種隔離級:
READ INCOMITTED (讀取未提交內容)
在READ UNOMITTED隔離級,所有事務都可以“看到" 木提交事務的執行結果。在這種級別上,可能會產生很多問題,除非用戶真的知道自己在做什么,井有很好的理由選擇這樣做。本隔離級很少用於實際應用,因為它的性能也不比其他級別好多少,而別的級別還有其他更多的優點。讀取未提交數據,也被稱之為“臟讀”(Dity Read)。
READ COMITTED (讀取提交內容)
大多數數據庫系統的默認陽離級是READ COMMITTED (但這不是MySQL默認的!)。它滿足了限離的早先簡單定義:一個事務在開始時,只能“看見”已經提交事務歷做的改變,一個事務從開始到提交前,所做的任何數據改變都是不可見的,除非已經提交。這種隔離級別也支持所謂的“不可重復讀”(NonrepeatableRead)。 這意味着用戶運行同一語句兩次,看到的結果是不同的。
REPEATABLE RSAD (可重讀)
REPEATABLE READ 隔離級解決了READ UNCOMNITTED隔離級導致的問題。它確保同一事務的多個實例在並發讀取數據時,會“看到同樣的"數據行。不過理論上,這會導致另一個棘手問題:幻讀(Phantom Read).簡單來說,幻讀指當用戶讀取某-范圍的數據行時,另-個事務又在該范圍內插人了新行,當用戶再讀取該范圍的數據行時,會發現有新的“幻影”(Phantom) 行。ImoDB 和Falco存律引擎通過名版本並發物)制(Mutiersion Comcurency Conira)機制解決了幻讀問題。本章后面將進一步討論這部分內容
REPEATABLE READ 是MySQL的默認事務隔離級。InnoDB 和Falcon存儲引擎都遵循這種設置。可以通過 select @@global.tx_isolation;來查看當前隔離級別。可參考其他資料,了解如何改變這種設置。其他一些存儲引擎也以此為默認設置,不過具體設置還要看相關引擎的具體規定。
SERIALIZABLE (可串行化)
SERIALIZABLE是最高級別的隔離級,它通過強制事務排序,使之不可能相互沖突,從而解決幻讀問題。簡言之,SERIALIZABLE 是在每個讀的數據行上加鎖。在這個級別,可能導致大量的超時(Timeout) 現象和鎖競爭(Lock Contention現象。作者很少看到有用戶選擇這種隔離級。但如果用戶的應用為了數據的穩定性,需要強制減少並發的話,也可以選擇這種隔離級。《高性能MySQL》
四種隔離級別存在的問題
是否會造成臟讀,不可重復讀或幻讀,是否加鎖讀
事務隔離級別 | 臟讀 | 不可重復讀 | 幻讀 | 加鎖 |
讀未提交 | 是 | 是 | 是 | 否 |
讀已提交 | 否 | 是 | 是 | 否 |
可重復讀 | 否 | 否 | 是 | 否 |
串行化 | 否 | 否 | 否 | 是 |
【end】