1. 程式人生 > >資料庫之事務的隔離級別(學習筆記)

資料庫之事務的隔離級別(學習筆記)

最近都在看資料庫的相關,然後就有看到事務,看了很多文章發現內容多,有些地方容易混淆,不做筆記還真不行,必須總結。

概念怎麼說?至少了解一波吧

事務可以理解為一個 獨立的工作單元, 在這個獨立的工作單元中, 有一組操作;放在事務(獨立工作單元)中的多個操作, 要麼全部執行成功, 要麼全部執行失敗

ACID(事務四大特性)

  • 原子性(Atomicity) 一個事務必須被視為一個不可分割的最小工作單元, 整個事務中的所有操作要麼全部提交成功, 要麼全部失敗回滾。對於一個事務來說, 不能只成功執行其中的一部分操作, 這就是事務的原子性。

  • 一致性(Consistency)

    • 雖然可資料表中的資料可能一直在變化, 但是事務的一致性特性會保證 資料庫總是從一個一致性的狀態 轉換到 另一個一致性的狀態;
    • 比如一個轉賬的例子 A初始200 ,B初始300,A給B轉100。

    轉賬前的一致性狀態是: ‘A’(餘額200), ‘B’(餘額300) 轉賬成功後的一致性狀態是: ‘A’(餘額100), ‘B’(餘額400) 轉賬如果失敗的話, 一致性的狀態應該回滾到轉賬前的狀態: ‘A’(餘額200), ‘B’(餘額300)

  • 隔離性(Isolation)

    • 隔離性是當多個使用者併發訪問資料庫時,比如同時操作同一張表時,資料庫為每一個使用者開啟的事務,不能被其他事務的操作所幹擾,多個併發事務之間要相互隔離。
    • 事務有四種隔離級別(從低到高: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE)
  1. 永續性(Durability) 永續性是指一個事務一旦被提交了,那麼對資料庫中的資料的改變就是永久性的,即便是在資料庫系統遇到故障的情況下也不會丟失提交事務的操作。

為什麼要設定隔離級別?

在資料庫操作中,在併發的情況下可能出現如下問題:

  • 更新丟失(Lost update) 如果多個執行緒操作,基於同一個查詢結構對錶中的記錄進行修改,那麼後修改的記錄將會覆蓋前面修改的記錄,前面的修改就丟失掉了,這就叫做更新丟失。這是因為系統沒有執行任何的鎖操作,因此併發事務並沒有被隔離開來。

    (A)第一類:事務A撤銷回滾時,把已經提交的事務B的更新資料覆蓋了。 image_1cn3bils8kl6rp1p5j1176fdv9s.png-31.4kB 在資料庫的隔離級別裡,該類的丟失更新不會出現

    (B)第二類:事務A覆蓋事務B已經提交的資料,造成事務B所做的操作丟失。。 image_1cn3bi7ubeapeh41opu1roflr48v.png-31.2kB 此類更新丟失問題, 無法依靠前三種隔離級別來解決, 只能用最高隔離級別 Serializable 或者手動使用樂觀鎖, 悲觀鎖來解決。

  • 髒讀(Dirty Reads)

    A事務讀取B事務尚未提交的資料並在此基礎上操作,而B事務執行回滾,那麼A讀取到的資料就是髒資料。

    image_1cn3b64hu1a0g19avvbr671og73d.png-29.3kB 在常見資料庫中, 事務已經用自身特性(隔離性的 – REPEATABLE READ或以上隔離級別)解決了這個問題; READ COMMITED級別保證了, 只要是當前語句執行前已經提交的資料都是可見的。注意和REPEATABLE READ級別的區!!!

  • 不可重複讀(Non-repeatable Reads)

    1. 假設現在上面的 髒讀問題已經被完全解決了, 那就意味著事務中每次讀取到的資料都是 永續性 的資料(被別的事務最終 提交/回滾 完成後的資料)。
    2. 但是你還需要知道的是: 解決了髒讀問題, 只是能保證你在事務中每次讀到的資料都是永續性的資料而已!!!
    3. 如果在一個事務中多次讀取同一個資料, 正好在兩次讀取之間, 另外一個事務確實已經完成了對該資料的修改並提交, 那問題就來了: 可能會出現多次讀取結果不一致的現象。

    image_1cn3b5oied31gaq1c3gi4m9930.png-25.1kB 在常見資料庫中, 事務已經用自身特性(隔離性的 – REPEATABLE READ或以上隔離級別)解決了這個問題; REPEATABLE READ級別保證了, 只要是當前事務執行前已經提交的資料都是可見的。注意和READ COMMITED級別的區!!!

  • 幻象讀

    • 容易搞混不可重複讀幻讀 ,都是說兩次讀取資料不一致。
    • 不可重複讀主要是說多次讀取一條記錄, 發現該記錄中某些列值被修改過。
    • 幻讀主要是說多次讀取一個範圍內的記錄(包括直接查詢所有記錄結果或者做聚合統計), 發現結果不一致(標準檔案一般指記錄增多(一般來講), 記錄的減少應該也算是幻讀(猜測))。

    指兩次執行同一條 select 語句會出現不同的結果,第二次讀會增加一資料行,並沒有說這兩次執行是在同一個事務中。 通俗的講,一個執行緒中的事務讀取到了另外一個事務insert的資料。 image_1cn3bh4iegsqpcnrrf6dboa72.png-21.7kB 在常見資料庫中, 事務已經用自身特性(隔離性的 – SERIALIZABLE)解決了這個問題。但是該隔離級別,一般不常用,高併發效率太低 比如Mysql Innodb 引擎在隔離級別為REPEATABLE READ(可重複讀)下,一般使用MVCC多版本控制來解決幻讀問題。 MySQL-InnoDB-MVCC多版本併發控制

事務的隔離級別

上面有提到,這裡再次列出,加深影響。事務的隔離級別有四種,由低到高依次

  • Read uncommitted (未授權讀取、讀未提交)
  • Read committed(授權讀取、讀提交)
  • Repeatable read(可重複讀取)
  • Serializable(序列化)

  隔離級別高的資料庫的可靠性高,但併發量低,而隔離級別低的資料庫可靠性低,但併發量高,系統開銷小。

image_1cn3bkt7k12sg159t1t4q19d7obra9.png-22.6kB

READ UNCIMMITTED (未提交讀)

  如果一個事務已經開始寫資料,則另外一個事務則不允許同時進行寫操作,但允許其他事務讀此行資料。 該隔離級別可以通過排他寫鎖實現。這樣就避免了更新丟失,卻可能出現髒讀。也就是說事務B讀取到了事務A未提交的資料

ep:

一個售票系統,A和B是售票員,他們分別是兩個不同視窗的員工,現在售票系統只剩下3張票,此時小明來A這裡買3張票,小張來B買票,A查到餘票還有就給接了訂單,就要執行第三步的時候,B接到小張的請求查詢有沒有餘票。B看到A賣出了3張票,於是拒絕賣票。但是A系統出了問題,第三步執行失敗,資料庫為保證原子性,資料進行了回滾,也就是說一張票都沒賣出去。

總結:這就是事務還沒提交,而別的事務可以看到他其中修改的資料的後果,也就是髒讀。

READ CIMMITTED (提交讀)

  讀取資料的事務允許其他事務繼續訪問該行資料,但是未提交的寫事務將會禁止其他事務訪問該行。

該隔離級別避免了髒讀,但是卻可能出現不可重複讀。事務A事先讀取了資料,事務B緊接了更新了資料,並提交了事務,而事務A再次讀取該資料時,資料已經發生了改變。

大多數資料庫系統的預設隔離級別是READ CIMMITTED。 ep:

還是A和B銷售員,餘票4張,小明來A請求3張訂票單,A受訂單,要賣出3張票,上面的銷售步驟執行中的時候,小張也來B那裡買票,由於A的銷售事務執行到一半,B事務沒有看到A的事務執行,讀到的票數是3,準備接受訂單的時候,A的銷售事務完成了,此時B的系統變成顯示0張票,此時只能拒絕訂單了。

總結:這就是A的事務執行到一半,而B看不到他執行的操作,所以看到的是舊資料,也就是不可重複讀

Repeatable Read(可重複讀)

  可重複讀是指在一個事務內,多次讀同一資料。在這個事務還沒有結束時,另外一個事務也訪問該同一資料。   在第一個事務中的兩次讀資料之間,即使第二個事務對資料進行修改,第一個事務兩次讀到的的資料是一樣的。這樣就發生了在一個事務內兩次讀到的資料是一樣的,因此稱為是可重複讀。 讀取資料的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。這樣避免了不可重複讀取和髒讀,但是有時可能出現幻象讀。(讀取資料的事務)這可以通過“共享讀鎖”和“排他寫鎖”實現。

ep:

銷售部門有規定,如果銷售記錄低於規定的值,要扣工資,此時經理在後端控制檯查看了一下小明的銷售記錄,發現銷售記錄達不到規定的次數,心裡暗喜,準備列印好銷售清單,理直氣壯和小明提出,沒想到打印出來的時候發現銷售清單裡面銷售數量增多了幾條,剛剛好達到要求,氣的經理撕了清單紙。原來是小明在就要列印的瞬間賣出了幾張票,因此避過了減工資的血光之災。

  雖然讀取同一條資料可以保證一致性,但是卻不能保證沒有插入新的資料,就是幻讀。

Serializable(序列讀)

提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,但不能併發執行。

  如果僅僅通過行級鎖是無法實現事務序列化的,必須通過其他機制保證新插入的資料不會被剛執行查詢操作的事務訪問到。序列化是最高的事務隔離級別,同時代價也花費最高,效能很低,一般很少使用,在該級別下,事務順序執行,不僅可以避免髒讀、不可重複讀,還避免了幻讀。

  對於多數應用程式,可以優先考慮把資料庫系統的隔離級別設為Read Committed。它能夠避免髒讀取,而且具有較好的併發效能。儘管它會導致不可重複讀、幻讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,可以由應用程式採用悲觀鎖或樂觀鎖來控制。大多數資料庫的預設級別就是Read committed,比如Sql Server , OracleMySQL的預設隔離級別就是Repeatable read