數據庫事務的隔離級別


數據庫事務( 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 (讀未提交):最低級別,任何情況都無法保證。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM