1. 程式人生 > 其它 >資料庫事務的隔離級別

資料庫事務的隔離級別

資料庫事務( transaction)是訪問並可能操作各種資料項的一個數據庫操作序列,這些操作要麼全部執行,要麼全部不執行,是一個不可分割的工作單位。事務由事務開始與事務結束之間執行的全部資料庫操作組成。 --百度百科

眾所周知,事務有ACID特性(原子性、一致性、隔離性、永續性),其中隔離性又包括四個級別:

  1. READ_UNCOMMITTED:讀未提交, 這種隔離級別指的是A事務可以讀取到B事務還沒有提交的資料,顯然這種隔離級別是不安全的,它容易引發不可重複讀、髒讀和幻讀。
  2. READ_COMMITTED:讀已提交,這種隔離級別指的是A事務只能讀取到B事務已經提交的資料,這種隔離級別比上面一種安全一些,但是隻能防止髒讀。
  3. REPEATABLE_READ:可重複讀,雖然名字叫可重複讀,但是它的意思是:重複讀同一條資料的情況下,能保證讀的結果是一致的。不少人被這個名字誤解,它的原理大致是A事務在開始後會將資料庫中的資料假凍結,也就是說無論B事務對資料作什麼樣的改變,A事務讀到的資料都是事務開始時的樣子,這樣就做到了無論讀多少次資料都是一致的。但是這種情況下還是會有幻讀的情況出現
  4. SERIALIZABLE:序列化,這種隔離級別也是最高的,所有的事務都必須像單執行緒一樣,一個個執行。它可以防止幻讀。

四種隔離級別中,併發程式設計下:1的效能最高,但是最不安全,4的效能最低,但是最安全,mysql中的預設隔離級別是REPEATABLE_READ。

檢視mysql8.0的事務隔離級別

/*檢視當前會話的事務隔離級別*/
select @@transaction_isolation;
/*檢視mysql的事務隔離級別*/
select @@global.transacton_isolation;

修改mysql8.0中的事務隔離級別

/*設定當前會話的隔離級別 */
/*設定read uncommitted級別*/
set session transaction isolation level read uncommitted;
/*設定read committed級別*/
set session transaction isolation level read
committed; /*設定repeatable read級別*/ set session transaction isolation level repeatable read; /*設定serializable級別*/ set session transaction isolation level serializable; /*設定mysql全域性的隔離級別 */ /*設定read uncommitted級別*/ set global transaction isolation level read uncommitted; /*設定read committed級別*/ set global transaction isolation level read committed; /*設定repeatable read級別*/ set global transaction isolation level repeatable read; /*設定serializable級別*/ set global transaction isolation level serializable;

---------------------------------------------------------------------------------下面是例項演示:-----------------------------------------------------------------------------------------------------------

下面將用例項來演示四種隔離級別以及可能出現的問題:

首先先新增一張Student表, 表中有id,name,score欄位,並填入測試欄位

1.READ_UNCOMMITTED

然後我們開啟兩個會話,並將兩個會話的事務隔離級別都設定為read_uncommitted

我們在left會話中先開啟一個事務,然後查詢s1的分數,此時可以看到分數是10

然後在right會話中再開啟一個事務,然後把s1的分數改為99,但是先不要提交,也中要回滾

然後我們再去left會話中單獨執行查詢s1的分數語句,可以看到明明事務還沒有提交, 卻可以看到被修改成了99。這就是髒讀

2.READ_COMMITTED

還是開啟兩個視窗模擬兩個事務。 然後將兩個視窗的事務隔離級別都修改為read_committed,因為修改方式和上面一樣就不放圖了。

然後還是在left中查詢s1的成績

然後再次在right中修改為99

再次執行在left中執行的話,可以看到還是10,因為我們改變了事務的隔離級別,right沒有提交的修改我們在left中看不到,這種隔離級別就防止了髒讀的出現

然後我們執行right的commit

再次在left中查詢s1,可以看到成績變為了99。因為right已經提交了事務,所以可以在left中看到,但是這又出現了left事務中同一條語句查詢兩次而得到的查詢結果不一致的問題,這就是資料的不可重複讀問題。

其實我覺得這個名字很容易誤導很多人, 要是翻譯成 "讀取結果不一致" 的話可以更貼近事實

3.REPEATABLE_READ

可以從這個隔離級別的字面意思上理解“可重複讀”,這個可重複讀可以解決就可以解決不可重複讀的問題。

我們還是先將left和right的隔離級別都設定為repeatable_read

和之前一樣的,還是先在left中開啟事務,查詢s1

然後在right中開啟事務,修改為100,最後直接提交

然後我們返回到left中檢視,可以看到雖然right的事務提交了,但是left中的結果依然是99。這就是repeatable_read這個隔離級別解決了不可重複讀的問題(其實換一種更直接的說法就是:repeatable_read這個隔離級別解決了多次讀取資料不一致的問題,在一個事務當中,多次讀取同一條資料,無論如何都是相同的)

但是這種隔離級別還是會有幻讀的情況出現,比如說:先在left中開啟事務, 檢視所有的學習成績

此時可以看到一共有s1和s2兩條資料,然後我們去right中將s2刪掉

然後再回到left中檢視,還是兩條,沒有任何問題,因為repeatable_read會將當前事務中的資料進行假凍結

然後這時我們將s2的成績修改成100,這時可以看到這個update語句的影響行數為0,這時我們再去重新執行select語句可以發現明明是有s2的,但是這條更新語句就是沒有效果,就像我們看到的s2是幻覺一樣,這就是幻讀

4.SERIALIZABLE

從字面意思上理解的話是序列化、序列化。 但是這裡指的其實是所有的事務必須一個個的按順序執行。這種隔離級別可以有效的防止以上任意一種錯誤出現

還是先將left和right的隔離級別都改為serializable

我們再重現剛剛的場景:先在left開啟一個事務,然後查詢student

然後在right中刪除s2,可以看到right就一直是正在處理

如果一直等待的話這就會因為超時而無法執行,因為這個隔離級別的所有事務都必須像單執行緒一樣一個個順序執行,沒有辦法同時處理,只要left不提交,right就永遠沒有辦法執行。所以right沒有辦法將s2刪除, 自然就不會有幻讀的情況出現了。不過正如我們看到的那樣,因為隔離級別太高了,導致事務只能按順序執行,這種的效能是非常差的。所以mysql退而求其次選擇了可重複讀(也就是多次讀取時結果一致)這種隔離策略

最後總結一下:

  ① Serializable (序列化):可避免髒讀、不可重複讀、幻讀的發生。

  ② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。

  ③ Read committed (讀已提交):可避免髒讀的發生。

  ④ Read uncommitted (讀未提交):最低級別,任何情況都無法保證。

ps:本文靈感來自b站江南一點雨江南一點雨的個人空間_嗶哩嗶哩_bilibili,他的資料庫事務隔離級別的視訊講的特別清楚,推薦大家去看看