這篇文章帶給大家的內容是關於如何理解資料庫事務隔離等級及髒讀、不可重複讀、幻讀,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。
1.1ACID原則。
ACID原則是資料庫事務正常執行的四個基本要素,分別指原子性、一致性、獨立性及持久性。
事務的原子性(Atomicity)是指一個事務要么全部執行,要么不執行,也就是說一個事務不可能只執行了一半就停止了,比如你從取款機取錢,這個事務可以分成兩個步驟:1劃卡,2出錢。不可能劃了卡,而錢卻沒出來,這兩步必須同時完成.要么就不完成。
交易的一致性(Consistency)是指交易的運作並不會改變資料庫中資料的一致性。例如,完整性約束了a b=10,一個事務改變了a,那麼b也應該隨之改變。或者說,A給B轉帳300元錢,那麼A的帳戶就必須是減少300元錢,B的帳戶就必須是增加300元錢,不能說是增加或減少瞭如200元錢等,這裡符合事務的原子性,但是不符合交易的一致性。往實際業務中沒有這麼簡單,往是類似買東西扣庫存這類的邏輯,主表裡有庫存,庫存表裡有庫存,SKU表裡還有,然後就因為設計缺陷,就算加了事務還是出現了超賣、SKU庫存對不上總庫存的問題,這個就是一致性不滿足的了。
獨立性(Isolation):事務的獨立性也有稱作隔離性,是指兩個以上的事務不會出現交錯執行的狀態,因為這樣可能會導致資料不一致。
持久性(Durability):一旦交易提交或回滾,這個狀態都要持久化到資料庫中,不考慮隔離性會出現的讀取問題。
1.2髒讀、不可重複讀,幻讀。
髒讀(Dirty read):在一個事務中讀取到另一個交易沒有提交的資料。例如,當一個事務正在存取數據,並且對數據進行了修改,而這種修改還沒有提交到資料庫中,這時,另一個事務也訪問這個數據,然後使用了這個數據。
不可重複讀取(NonRepeatable Read):既不能讀到相同的資料內容。是指在一個事務內,多次讀同一數據,在這個事務還沒有結束時,另外一個事務也訪問該同一數據並且修改,那麼,在第一個事務中的兩次讀數據之間,由於第二個事務的修改,第一個事務兩次讀到的的資料可能是不一樣的。
幻讀(Phantom Read):在一個交易中,兩次查詢的結果不一致(針對的insert操作) 。是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,這種修改涉及到表中的全部資料行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,以後操作第一個事務的使用者發現表中還有沒有修改的資料行,就好像發生了幻覺一樣。
例如,一個編輯人員更改作者提交的文檔,但當生產部門將其更改內容合併到該文檔的主複本時,發現作者已將未編輯的新材料添加到該文檔中。如果在編輯人員和生產部門完成原始文件的處理之前,任何人都無法將新資料新增至文件中,則可以避免該問題。
資料庫交易的隔離等級有4個,由低到高依序為Read uncommitted(讀未提交)、Read committed(讀取提交) 、Repeatable read(可重複讀取)、Serializable(序列化),這四個層級可以逐一解決髒讀、不可重複讀取、幻讀這幾類問題。
2.1 Read uncommitted(讀未提交)
公司發工資了,領導把5000元打到singo的帳號上,但是該事務並未提交,而singo剛好去查看帳戶,發現薪水到帳5000元整,非常高興。可是不幸的是,領導發現發給singo的工資金應該是2000元,於是迅速回滾了事務(將5000元回滾),修改金額後(修改為2000元),將事務提交,最後singo實際的薪水只有2000元,singo空歡喜一場。
出現上述情況,即我們所說的髒讀,兩個並發的事務,“事務A:領導給singo發工資”,“事務B:singo查詢工資賬戶”,事務B讀取了事務A尚未提交的數據。
當隔離等級設定為Read uncommitted(讀取未提交)時,就可能出現髒讀,如果我們此時將隔離等級提升為Read committed(讀取已提交),便可避免髒讀。
2.2 Read committed(讀取已提交)
#singo拿著工資卡去消費,系統讀取到卡里確實有2000元,而此時她的老婆也正好在網上轉賬,把singo工資卡的2000元轉到另一個賬戶,並在singo之前提交了事務,當singo扣款時,系統檢查到singo的工資卡已經沒有錢,扣款失敗,singo十分納悶,明明卡里有錢,到底是啥情況呢?
出現上述情況,即我們所說的不可重複讀,兩個並發的事務,“事務A:singo消費”、“事務B:singo的老婆網上轉賬”,事務A事先讀取了數據,事務B緊接了更新了數據,並提交了事務,而事務A再次讀取該數據時,數據已經發生了改變。
當隔離等級設定為Read committed(讀取已提交)時,避免了髒讀,但是可能會造成不可重複讀取(既不能讀到相同的資料內容)。
大多數資料庫的預設等級就是Read committed(讀取已提交),例如Sql Server , Oracle,此時如果將隔離等級提升為Repeatable read(可重複讀取),則可以避免髒讀和不可重複讀的發生。
2.3 Repeatable read(可重複讀取)
當隔離等級設定為Repeatable read(可重複讀取)時,可以避免無法重複讀取。當singo拿著薪資卡去消費時,一旦系統開始讀取薪資卡資訊(即事務開始),singo的老婆就不可能對該記錄進行修改,也就是singo的老婆不能在此時轉帳。
(這裡兩個部落格舉得例子不一樣,請各位看官指明原因)或者說,有A、B兩個會話,分別開啟兩個事務,然後A向B轉了500元錢,A 提交事務,B再去查看,發現依舊是原錢數,B只能結束當前事務,在開啟一個新事務,才能查詢到數據的變化,這樣便避免了不可重複讀。如果我們設定了Seriizable(序列化),就等於鎖定表,某一時間內只允許一個事務存取該表。
雖然Repeatable read避免了不可重複讀,但還有可能出現幻讀 。
例如singo的老婆工作在銀行部門,她時常透過銀行內部系統查看singo的信用卡消費記錄。有一天,她正在查詢到singo當月信用卡的總消費金額(select sum(amount) from transaction where month = 本月)為80元,而singo此時正好在外面胡吃海塞後在收銀台買單,消費1000元,即新增了一張1000元的消費記錄(insert transaction … ),並提交了事務,隨後singo的老婆將singo當月信用卡消費的明細打印到A4紙上,卻發現消費總額為1080元,singo的老婆很詔異,以為出現了幻覺,幻讀就這樣產生了。
註:Mysql的預設隔離等級就是Repeatable read。
2.4 Serializable(序列化)
Serializable(序列化)是最高的事務隔離級別,同時代價也花費最高,性能很低,一般很少使用,在該層級下,事務順序執行,不僅可以避免髒讀、不可重複讀,還避免了幻讀。
3.1 隔離等級與對應可能產生的問題表
隔離等級 | 髒讀(Dirty read) | 不可重複讀取(NonRepeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
#讀取未提交 (Read uncommitted) |
可能 | 可能 | 可能 |
|
|||
|
|||
|
#讀取已提交 | (Read committed) |
以上是如何理解資料庫事務隔離等級及髒讀、不可重複讀、幻讀的詳細內容。更多資訊請關注PHP中文網其他相關文章!