資料庫事務隔離級別及髒讀、不可重複讀、幻讀的理解
一、資料庫事務正確執行的四個基本要素
1.1ACID原則。
ACID原則是資料庫事務正常執行的四個基本要素,分別指原子性、一致性、獨立性及永續性。
原子性(Atomicity)是指一個事務要麼全部執行,要麼不執行,也就是說一個事務不可能只執行了一半就停止了,比如你從取款機取錢,這個事務可以分成兩個步驟:1劃卡,2出錢。不可能劃了卡,而錢卻沒出來,這兩步必須同時完成.要麼就不完成。
一致性(Consistency)是指事務的執行並不改變資料庫中資料的一致性。例如,完整性約束了a+b=10,一個事務改變了a,那麼b也應該隨之改變。或者說,A給B轉賬300元錢,那麼A的賬戶就必須是減少300元錢,B的賬戶就必須是增加300元錢,不能說是增加或減少了如200元錢等,這裡符合事務的原子性,但是不符合事務的一致性。往實際業務中沒有這麼簡單,往是類似買東西扣庫存這類的邏輯,主表裡有庫存,庫存表裡有庫存,SKU表裡還有,然後就因為設計缺陷,就算加了事務還是出現了超賣、SKU庫存對不上總庫存的問題,這個就是一致性不滿足的了。
隔離性(獨立性)(Isolation):事務的獨立性也有稱作隔離性,是指兩個以上的事務不會出現交錯執行的狀態,因為這樣可能會導致資料不一致。
永續性(Durability):一旦事務提交或者回滾,這個狀態都要持久化到資料庫中,不考慮隔離性會出現的讀問題。
二、事務併發處理帶來的問題
2.1髒讀、不可重複讀,幻讀,更新丟失。
髒讀(Dirty read):在一個事務中讀取到另一個事務已經修改但沒有提交的資料。例如,事務A對資料進行了修改,但是還沒有提交,這時事務B讀取這個資料,然後事務A回滾,那麼事務B取的資料無效。不符合一致性
。
不可重複讀(NonRepeatable Read):既不能讀到相同的資料內容,事務A讀取到了事務B已經提交的修改
不符合隔離性
。
幻讀(Phantom Read):事務A讀取到了事務B已經提交的新增
資料。在一個事務中,兩次查詢的結果不一致(針對的insert操作) 。例如事務A對一個表中的資料進行了修改,同時,事務B也修改這個表中的資料,這種修改是向表中插入一行新資料。那麼,事務A的使用者後面的操作發現表中還有沒有修改的資料行,就好象發生了幻覺一樣。不符合隔離性
。
更新丟失(Update lose):兩個事務同時操作相同資料,後提交的事務會覆蓋先提交的事務處理結果。通過樂觀鎖就可以解決
三、資料庫事務隔離級別
資料庫事務的隔離級別有4個,由低到高依次為Read uncommitted(讀未提交)、Read committed(讀提交) 、Repeatable read(可重複讀)、Serializable(序列化),這四個級別可以逐個解決髒讀 、不可重複讀 、幻讀這幾類問題。
3.1 Read uncommitted(讀未提交)
公司發工資了,領導把5000元打到singo的賬號上,但是該事務並未提交,而singo正好去檢視賬戶,發現工資到賬5000元整,非常高興。可是不幸的是,領導發現發給singo的工資金應該是2000元,於是迅速回滾了事務(將5000元回滾),修改金額後(修改為2000元),將事務提交,最後singo實際的工資只有 2000元,singo空歡喜一場。
出現上述情況,即我們所說的髒讀 ,兩個併發的事務,“事務A:領導給singo發工資”,“事務B:singo查詢工資賬戶”,事務B讀取了事務A尚未提交的資料。
當隔離級別設定為Read uncommitted(讀未提交)時,就可能出現髒讀,如果我們此時將隔離級別提升為Read committed(讀已提交),便可避免髒讀。
3.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(可重複讀),可以避免髒讀和不可重複讀的發生。
3.3 Repeatable read(可重複讀)
當隔離級別設定為Repeatable read(可重複讀)時,可以避免不可重複讀。
有A、B兩個會話,分別開啟兩個事務,B檢視餘額是100元錢,然後A向B轉了500元錢,A 提交事務,B再去檢視,發現依舊是100元錢,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。
3.4 Serializable(序列化)
Serializable(序列化)是最高的事務隔離級別,同時代價也花費最高,效能很低,一般很少使用,在該級別下,事務順序執行,不僅可以避免髒讀、不可重複讀,還避免了幻讀。
四、總結
4.1 隔離級別與對應可能產生的問題表
隔離級別 | 髒讀(Dirty read) | 不可重複讀(NonRepeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
讀未提交 (Read uncommitted) |
可能 | 可能 | 可能 |
讀已提交 (Read committed) |
不可能 | 可能 | 可能 |
可重複讀 (Repeatable read) |
不可能 | 不可能 | 可能 |
序列化 (Serializable) |
不可能 | 不可能 | 不可能 |
五、一些資料庫鎖的筆記等
以下觀點並非正確,大家可開啟多個連線進行實踐。
5.1、鎖定義和分類
鎖定義:
鎖是計算機協調多個程序和執行緒併發訪問某一資源的機制。
鎖分類:
(1) 按效能分類:樂觀鎖和悲觀鎖。
(2) 按操作分類:讀鎖和寫鎖。
(3) 按粒度分類:表鎖和行鎖。
InnoDB與MyISAM的不同點:
(1)前者支援事務(Transaction)。
(2)採用了行級鎖。
5.2、一些總結筆記
MyISAM在執行查詢語句(SELECT)前會自動給涉及的所有表加讀鎖,在執行增刪改操作前,會自動給涉及的所有表加寫鎖。
讀鎖:一個會話給表加了讀鎖,沒有釋放,那麼當前會話及其他會話就無法寫,只能讀。
寫鎖:一個會話給表加了寫鎖,沒有釋放,那麼當前會話可以對錶讀和寫,其他會話不能讀也不能寫。
InnoDB行鎖:
只對當前行加鎖,一個會話開啟事務,然後進行更新,另一個會話也要對這行資料進行更新,那麼會等前面的會話提交才能更新。
當隔離級別可重複讀時:
第一次查詢會有快照,比如事務A更新了資料,但是事務B還沒有執行查詢操作,然後B查詢,那麼B查詢的資料是事務A更新後的,以後每次查詢,都是查詢這次的快照。比如金額原先為100元,事務A將之更新為200,事務B查詢的時候,是200,如果事務B先查詢,事務A後更新,那麼事務B查詢到的還是100。但即使查詢的資料是100(事務B已經更新成了200),如果事務A對金額進行加200的操作,那麼結果是400,不是300,所以說,可重複讀取不將查詢結果(100)拿來當作變數運算的話,其執行結果是正確的。
幻讀,也是在其他事務修改資料後,才進行讀取,才能讀到其他事務已提交的資料。