Mysql四種事務(wù)隔離級(jí)別
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
? 作者?|? 玉樹臨楓
來源 |? urlify.cn/jmM3ya
76套java從入門到精通實(shí)戰(zhàn)課程分享
一、前提
時(shí)過一年重新拾起博文記錄,希望后面都能堅(jiān)持下來。接著之前MySql的學(xué)習(xí),先記錄下這篇。
以下都是基于mysql8 innodb存儲(chǔ)引擎進(jìn)行分析的。
二、事務(wù)的ACID特性
A(Atomicity) 原子性
指整個(gè)數(shù)據(jù)庫(kù)事務(wù)是不可分割的單位,整個(gè)事務(wù)中的所有操作要么全部提交成功,要么全部失敗回滾。只有使事務(wù)中所有的數(shù)據(jù)庫(kù)操作都執(zhí)行成功,才算整個(gè)事務(wù)執(zhí)行成功。否則事務(wù)中任何一個(gè)SQL語句執(zhí)行失敗,那么這個(gè)事務(wù)就是執(zhí)行失敗的, 已執(zhí)行成功的SQL語句也必須撤銷,數(shù)據(jù)庫(kù)狀態(tài)應(yīng)該退回到執(zhí)行事務(wù)前的狀態(tài)。
C(consistency) 一致性
一致性事務(wù)將數(shù)據(jù)庫(kù)從一種狀態(tài)轉(zhuǎn)變?yōu)橄乱环N一致的狀態(tài)。在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫(kù)的完整性約束沒有被破壞。
例如:在表中有個(gè)字段為姓名為唯一約束,即在表中姓名不能重復(fù)。如果一個(gè)事務(wù)對(duì)姓名字段進(jìn)行了修改,但是在事務(wù)提交或事務(wù)操作發(fā)生回滾后,表中的姓名變得非唯一了,這就破壞了事務(wù)的一致性要求,即事務(wù)將數(shù)據(jù)從一種狀態(tài)變?yōu)榱艘环N不一致的狀態(tài)。
I(isolation) 隔離性
事務(wù)的隔離性要求每個(gè)讀寫事務(wù)的對(duì)象對(duì)其他事務(wù)的操作對(duì)象能相互分離,即該事務(wù)提交前對(duì)其他事務(wù)都不可見。通常可以使用鎖來實(shí)現(xiàn)。
D(durability) 持久性
事務(wù)一旦提交,其結(jié)果就是永久性的。即使發(fā)生宕機(jī)等故障,數(shù)據(jù)庫(kù)也能將數(shù)據(jù)恢復(fù)。
需要注意的是:只能從事務(wù)本身的角度來保證結(jié)果的永久性,例如:在事務(wù)提交后,所有變化都是永久的。即使當(dāng)數(shù)據(jù)因?yàn)楸罎⒍枰謴?fù)時(shí),也能保證恢復(fù)后提交的數(shù)據(jù)都不會(huì)丟失。但如果不是數(shù)據(jù)庫(kù)本身發(fā)生故障,而是一些外部的原因?qū)е聰?shù)據(jù)庫(kù)發(fā)生問題,則有可能是提交的數(shù)據(jù)丟失(RAID卡損壞)。
因此持久性保證事務(wù)系統(tǒng)的高可靠性,而不是高可用性。對(duì)于高可用性的實(shí)現(xiàn),事務(wù)本身并不能保證,需要一些系統(tǒng)共同配合完成。
三、事務(wù)的4種隔離級(jí)別
Read Uncommitted - 未提交讀
在該隔離級(jí)別下的事務(wù)會(huì)讀取到其未提交事務(wù)的數(shù)據(jù),此種現(xiàn)象也稱之為臟讀。
| 步驟 | 事務(wù)1 | 事務(wù)2 |
| 1 | 設(shè)置隔離級(jí)別
mysql> set @@session.transaction_isolation
| |
| 2 | 開啟事務(wù)1 mysql> begin; | |
| 3 | 無需管隔離級(jí)別,只開啟事務(wù)2并插入記錄
mysql> begin;
| |
| ?4 | 此時(shí)能查到事務(wù)2中未提交事務(wù)中的數(shù)據(jù),這就是臟讀。
mysql> select * from t;
|
注意:設(shè)置隔離級(jí)別之前,記得查看當(dāng)前隔離級(jí)別的key, 有可能版本不一樣對(duì)應(yīng)的key不一樣。(另外表可以自己隨意選擇)

1 mysql> show variables like '%isolation%';
2 +-----------------------+------------------+
3 | Variable_name | Value |
4 +-----------------------+------------------+
5 | transaction_isolation | READ-UNCOMMITTED |
6 +-----------------------+------------------+
7 1 row in set (0.00 sec)
在實(shí)際的業(yè)務(wù)場(chǎng)景中應(yīng)該都不允許臟讀出現(xiàn),既然這么評(píng)價(jià)不好為什么還會(huì)出現(xiàn)呢?但其實(shí)這個(gè)隔離級(jí)別下的數(shù)據(jù)庫(kù)并發(fā)性能是最好的。
?
Read Committed - 提交讀
一個(gè)事務(wù)可以讀取另一個(gè)已提交的事務(wù),多次讀取會(huì)造成不一樣的結(jié)果。這種現(xiàn)象也被稱為不可重復(fù)讀。舉例說明:
| 步驟 | 事務(wù)1 | 事務(wù)2 |
| 1 | 先設(shè)置隔離級(jí)別
mysql> set @@session.transaction_isolation
| |
| 2 | 開啟事務(wù)1,查詢t表記錄為空mysql> begin; | |
| 3 | 無需管隔離級(jí)別,只開啟事務(wù)2并插入記錄
mysql> begin;
| |
| 4 | 繼續(xù)查詢表t依然顯示為空 (沒有讀取到事務(wù)2未提交的數(shù)據(jù),所以不存在臟讀問題) mysql> select * from t; | |
| 5 | ?提交事務(wù)2 mysql> commit; | |
| 6 | 繼續(xù)查詢表t,此時(shí)能查詢事務(wù)2已提交的數(shù)據(jù)記錄(出現(xiàn)前后查詢不一致)
mysql> select * from t;
|
總結(jié):在隔離級(jí)別中解決了臟讀問題,但存在不可重復(fù)讀的問題(事務(wù)中會(huì)讀取其他已提交事務(wù)中的數(shù)據(jù))?
Repeatable Read - 可重復(fù)讀
該隔離級(jí)別是Innodb默認(rèn)的隔離級(jí)別:在同一個(gè)事務(wù)里select的結(jié)果是事務(wù)開始時(shí)間時(shí)間點(diǎn)的狀態(tài),解決了不可重復(fù)讀問題。
但其中會(huì)出現(xiàn)幻讀現(xiàn)象:基于可重復(fù)讀的基礎(chǔ)上查詢結(jié)果是一樣的,但是當(dāng)對(duì)某些行進(jìn)行更新或者插入時(shí)卻會(huì)受到影響操作不了,就形成了幻讀。例如:
| 步驟 | 事務(wù)1 | 事務(wù)2 |
| ?1 | 設(shè)置隔離級(jí)別
mysql> set @@session.transaction_isolation
| |
| ?2 | ?開啟事務(wù)1,并查詢
mysql> begin;
| |
| ?3 | ?無需管隔離級(jí)別,只開啟事務(wù)2并插入記錄
mysql> begin;
| |
| 4? | 繼續(xù)查詢表t依然只有一條記錄id=3 (沒有讀取到事務(wù)2未提交的數(shù)據(jù),所以不存在臟讀問題)
mysql> select * from t;
| |
| ?5 | ? 將第二個(gè)窗口中的事務(wù)提交。 mysql> commit; | |
| ?6 | 繼續(xù)查詢表t依然只有一條記錄id=3 (沒有讀取到事務(wù)2已提交的數(shù)據(jù),所以不存在不可重復(fù)讀問題)
mysql> select * from t;
| |
| ?7 | ? 接著插入一條4的記錄,但提示插入不了,提示主鍵沖突問題。 然而查詢卻沒有這條id=4記錄。這就是幻讀現(xiàn)象。
mysql> insert into t select 4, '4';
| |
| ?8 | ??另一種幻讀現(xiàn)象:接著上面操作,不插入記錄只更新記錄,將name 更新成 '5'后,發(fā)現(xiàn)有兩行受影響 | |
| ?
mysql> update t set name = '5';
| ||
總結(jié):在隔離級(jí)別中解決了臟讀問題、不可重復(fù)讀的問題,但會(huì)存在幻讀問題。
但I(xiàn)nnodb引擎提供了間隙鎖:innodb-next-key-locks, 解決了幻讀問題。基于上面結(jié)果演示下:
| 步驟 | 事務(wù)1 | 事務(wù)2 |
| 9 | 查詢時(shí)加上間隙鎖
mysql> begin;mysql> select * from t where id > 0 for update;
? | |
| ?10 | ?插入記錄為6的數(shù)據(jù)就會(huì)發(fā)現(xiàn)插入這條記錄獲取鎖超時(shí),自動(dòng)異常 insert into t select 6, '6';? |
這樣成功的避免了幻讀問題,阻止了其他事務(wù)可能影響到我當(dāng)前事務(wù)所涉及到的數(shù)據(jù)范圍。
Serializable - 可串行化
在該隔離級(jí)別下事務(wù)都是串行順序執(zhí)行的,MySQL 數(shù)據(jù)庫(kù)的 InnoDB 引擎會(huì)給讀操作隱式加一把讀共享鎖,從而避免了臟讀、不可重讀復(fù)讀和幻讀問題。
?
四、總結(jié)
1、臟讀:在一個(gè)事務(wù)中會(huì)讀取到其未提交事務(wù)的數(shù)據(jù),此種現(xiàn)象也稱之為臟讀
2、不可重復(fù)讀:一個(gè)事務(wù)可以讀取另一個(gè)已提交的事務(wù),多次讀取會(huì)造成不一樣的結(jié)果。這種現(xiàn)象也被稱為不可重復(fù)讀
3、幻讀:基于可重復(fù)讀的基礎(chǔ)上查詢結(jié)果是一樣的,但是當(dāng)對(duì)某些行進(jìn)行更新或者插入時(shí)卻會(huì)受到影響操作不了,就形成了幻讀。
隔離級(jí)別 | 臟讀 | 不可重復(fù)讀 | 幻讀 |
讀未提交(uncommitted read) | 可能出現(xiàn) | 可能出現(xiàn) | 可能出現(xiàn) |
讀提交(committed read) | 不會(huì)出現(xiàn) | 可能出現(xiàn) | 可能出現(xiàn) |
可重復(fù)讀(Repeatable Read) | 不會(huì)出現(xiàn) | 不會(huì)出現(xiàn) | 可能出現(xiàn)(加上間隙鎖就不會(huì)) |
可串行化(Serializable) | 不會(huì)出現(xiàn) | 不會(huì)出現(xiàn) | 不會(huì)出現(xiàn) |
五、參考文獻(xiàn)
《MySql 技術(shù)內(nèi)幕(Innodb)第二版》
https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
粉絲福利:Java從入門到入土學(xué)習(xí)路線圖
??????

??長(zhǎng)按上方微信二維碼?2 秒
感謝點(diǎn)贊支持下哈?
