1. 程式人生 > >資料庫事務隔離級別及髒讀、不可重複讀、幻讀的理解

資料庫事務隔離級別及髒讀、不可重複讀、幻讀的理解

一、資料庫事務正確執行的四個基本要素

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已經提交的修改

資料(一個事務範圍內兩個相同的查詢卻返回了不同資料)。例如事務A先讀取資料,然後事務B對該同一資料修改並提交,那麼事務A再次讀取該資料時,由於事務B對該資料的修改,事務A兩次讀到的的資料可能是不一樣的。不符合隔離性

  幻讀(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)拿來當作變數運算的話,其執行結果是正確的。

幻讀,也是在其他事務修改資料後,才進行讀取,才能讀到其他事務已提交的資料。