5分鐘帶你讀懂事務隔離性與隔離級別

前言

咱們在上一章節中介紹過數據庫的帶你瞭解數據庫中事務的ACID特性 的相關用法。本章節主要來介紹下數據庫中一個很是重要的知識點事務的隔離級別。若有錯誤還請你們及時指出~ mysql

問題:git

  • 事務的隔離級別有哪些?
  • 若是併發事務沒有進行隔離,會出現什麼問題?
如下都是採用mysql數據庫

在多個事務併發作數據庫操做的時候,若是沒有有效的避免機制,就會出現種種問題。大致上有如下問題:github

1、引起的問題

在併發事務沒有進行隔離的狀況下,會發生以下問題。sql

問題一:髒讀

髒讀指一個事務讀取了另一個事務未提交的數據。數據庫

具體看後文案例介紹編程

問題二:不可重複讀

不可重複讀指在一個事務內讀取表中的某一行數據,屢次讀取結果不一樣。
不可重複讀和髒讀的區別是,髒讀是讀取前一事務未提交的髒數據,不可重複讀是從新讀取了前一事務已提交的數據。微信

具體看後文案例介紹session

問題三:幻讀(虛讀)

幻讀(虛讀)指在一個事務內讀取到了別的事務插入的數據,致使先後讀取不一致。併發

具體看後文案例介紹性能

2、概念

2.1 事務的隔離級別分爲:

  • Read uncommitted(讀未提交)
  • Read Committed(讀已提交)
  • Repeatable Reads(可重複讀)
  • Serializable(串行化)

Read uncommitted

讀未提交:隔離級別最低的一種事務級別。在這種隔離級別下,會引起髒讀、不可重複讀和幻讀。

Read Committed

讀已提交讀到的都是別人提交後的值。這種隔離級別下,會引起不可重複讀和幻讀,但避免了髒讀。

Repeatable Reads

可重複讀這種隔離級別下,會引起幻讀,但避免了髒讀、不可重複讀。

Serializable

串行化是最嚴格的隔離級別。在Serializable隔離級別下,全部事務按照次序依次執行。髒讀、不可重複讀、幻讀都不會出現。

隔離級別

3、操做

3.1 查看事務隔離級別

SHOW VARIABLES LIKE 'tx_isolation';

查看全局的事務隔離級別

SHOW GLOBAL VARIABLES LIKE 'tx_isolation';

使用系統變量查詢

SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;

3.2 設置MysQL的事務隔離級別

語法

SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
  {
       REPEATABLE READ
     | READ COMMITTED
     | READ UNCOMMITTED
     | SERIALIZABLE
   }

GLOBAL:設置全局的事務隔離級別
SESSION:設置當前session的事務隔離級別,若是語句沒有指定GLOBAL或SESSION,默認值爲SESSION

使用系統變量設置事務隔離級別

SET GLOBAL tx_isolation='REPEATABLE-READ';
SET SESSION tx_isolation='SERIALIZABLE';

4、案例分析

下面實際操做中使用到的一些併發控制語句,可看上面的操做介紹

做爲演示:product表

productId productName productPrice productCount
1 xiaomi 1999 100

帶着上面的咱們來看一下,事務在沒有隔離性的狀況下,會引起哪些問題?

同時打開兩個窗口模擬2個用戶併發訪問數據庫

4.1 事務隔離級別設置爲read uncommitted

查詢事務隔離級別

SELECT @@tx_isolation;

設置隔離級別爲未提交讀

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
注意:須要同時修改兩個窗口的事務隔離級別

如下咱們以兩位用戶搶小米手機爲例

時間軸 事務A 事務B
T1 start transaction;
T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T3 start transaction;
T4 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T5 update product set productCount = 99 where productId = 1;
T6 select p.productName,p.productCount from product p where p.productId=1;(productCount =99)
T7 ROLLBACK;
T8 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)

T1—— A用戶開啓事務,start transaction;
T2—— A用戶查詢當前小米手機剩餘數量,select p.productName,p.productCount from product p where p.productId=1;此時數量顯示爲100。
T3——B用戶開啓事務,start transaction;
T4——B用戶查詢當前小米手機剩餘數量,select p.productName,p.productCount from product p where p.productId=1;此時數量顯示爲100。
T5—— B用戶購買了一臺小米手機,update product set productCount = 99 where productId = 1; 此時只修改數據並未提交事務。
T6—— A用戶刷新頁面,select p.productName,p.productCount from product p where p.productId=1;此時數量顯示爲99。
T7—— B用戶購買失敗,回滾事務。
T8—— A用戶查詢當前小米手機剩餘數量,select p.productName,p.productCount from product p where p.productId=1;此時數量顯示爲100。


小結:

事務A讀取了未提交的數據,事務B的回滾,致使了事務A的數據不一致,致使了事務A的髒讀

4.2 事務隔離級別設置爲Read Committed

查詢事務隔離級別

SELECT @@tx_isolation;

更改數據庫隔離級別,設置隔離級別爲提交讀

SET SESSION  TRANSACTION ISOLATION LEVEL READ COMMITTED;
注意:須要同時修改兩個窗口的事務隔離級別
時間軸 事務A 事務B
T1 start transaction;
T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T3 start transaction;
T4 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T5 update product set productCount = 99 where productId = 1;
T7 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T6 commit;
T8 select p.productName,p.productCount from product p where p.productId=1;(productCount =99)

這裏就再也不對流程作過多贅述。

小結:

能夠看到避免了髒讀現象,可是卻出現了,一個事務尚未結束,就發生了不可重複讀問題,即事務A來講 productCount從 100->100->99。但這個過程當中事務並未提交結束。

4.3 事務隔離級別設置爲Repeatable Read(mysql默認級別)

查詢事務隔離級別

SELECT @@tx_isolation;

更改數據庫隔離級別,設置隔離級別爲可重複讀

SET SESSION  TRANSACTION ISOLATION LEVEL REPEATABLE READ;
注意:須要同時修改兩個窗口的事務隔離級別
時間軸 事務A 事務B
T1 start transaction;
T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T3 start transaction;
T4 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T5 update product set productCount = 99 where productId = 1;
T7 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T6 commit;
T8 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)

這裏就再也不對流程作過多贅述。

小結:

能夠看到可重複讀隔離級別避免了髒讀不可重複讀的問題,可是出現了幻讀現象。事務A查詢到的小米數量等於100,可是事務B修改了數量爲99,可是事務A讀取到的值仍是100。當事務A去減1等於99時,是錯誤的,此時應該是99-1=98纔對。接下來咱們再提升一個事務隔離級別。

4.4 事務隔離級別設置爲Serializable

查詢事務隔離級別

SELECT @@tx_isolation;

更改數據庫隔離級別,設置隔離級別爲串行化

SET SESSION  TRANSACTION ISOLATION LEVEL REPEATABLE READ;
時間軸 事務A 事務B
T1 start transaction;
T2 start transaction;
T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100);
T4 update product set productCount = 99 where productId = 1;(等待中..)

這裏就再也不對流程作過多贅述。

小結:

在咱們Serializable隔離級別中,咱們能夠看到事務B去作修改動做時卡主了,不能向下執行。這是由於:給事務A的select操做上了鎖,因此事務B去修改值的話,就會被卡主。只有當事務A操做執行完畢,纔會執行事務B的操做。這樣就避免了上述三個問題了。

問題自己

  • 回到問題的自己,其實咱們並不須要將事務提到這麼高。
  • 問題的自己就是,當咱們讀完了的時候,就要在上面加鎖。咱們不但願別人可以去讀它。由於別人讀到了count,就會修改count的值,並寫進去。因此咱們在select 操做的時候,加上for update。這時候就會把這行操做給鎖掉了。那麼另一我的也進行相同的操做,也表示select 出來的count須要進行update,須要鎖住。
select p.productName,p.productCount from product p where p.productId=1 for update;
PS: 在實際開發過程當中,這樣的加鎖行爲,是很是的耗系統性能的。下一章節咱們未來介紹 悲觀鎖與樂觀鎖

文末

本章節主要介紹了數據庫中事務的ADID特性中的 隔離性,在沒有隔離的狀況下會發生什麼問題,相信你們經過本章,對數據庫事務中的 隔離性有了必定的瞭解,下篇文章咱們將介紹數據庫中的 悲觀鎖與樂觀鎖
歡迎關注公衆號: Coder編程
獲取最新原創技術文章和相關免費學習資料,隨時隨地學習技術知識!

微信公衆號

推薦閱讀

帶你瞭解數據庫中JOIN的用法

帶你瞭解數據庫中group by的用法

帶你瞭解數據庫中事務的ACID特性

Github我的主頁目錄

Gitee我的主頁目錄

歡迎你們關注並Star~

相關文章
相關標籤/搜索