好久沒碰數(shù)據(jù)庫了识藤,只是想起自己當(dāng)時在搞數(shù)據(jù)庫的時候在事務(wù)隔離級別這塊老是卡眯牧,似懂非懂的。現(xiàn)在想把這塊整理出來剪个,盡量用最簡潔的語言描述出來版确,供新人參考。
首先創(chuàng)建一個表account绒疗。創(chuàng)建表的過程略過(由于InnoDB存儲引擎支持事務(wù),所以將表的存儲引擎設(shè)置為InnoDB)惕虑。表的結(jié)構(gòu)如下:
然后往表中插入兩條數(shù)據(jù),插入后結(jié)果如下:
為了說明問題健提,我們打開兩個控制臺分別進(jìn)行登錄來模擬兩個用戶(暫且成為用戶A和用戶B吧)伟叛,并設(shè)置當(dāng)前MySQL會話的事務(wù)隔離級別。
一. read uncommitted(讀取未提交數(shù)據(jù))
具體用戶A的操作如下:
set session transaction isolation level read uncommitted紊遵;
start transaction;
select * from account;
結(jié)果如下:
用戶B的操作如下:
set session transaction isolation level read uncommitted暗膜;
start transaction;
update account set account=account+200 where id = 1;
隨后我們在A用戶中查詢數(shù)據(jù)鞭衩,結(jié)果如下:
結(jié)論一:
我們將事務(wù)隔離級別設(shè)置為read uncommitted,即便是事務(wù)沒有commit恒水,但是我們?nèi)匀荒茏x到未提交的數(shù)據(jù)饲齐,這是所有隔離級別中最低的一種。
那么這么做有什么問題嗎御雕?
那就是我們在一個事務(wù)中可以隨隨便便讀取到其他事務(wù)未提交的數(shù)據(jù)滥搭,這還是比較麻煩的,我們叫臟讀闽坡。我不知道這個名字是怎么起的愁溜,為了增強(qiáng)大家的印象,可以這么想冕象,這個事務(wù)好輕浮啊,饑渴到連別人沒提交的東西都等不及论悴,真臟,呸膀估!
實際上我們的數(shù)據(jù)改變了嗎?
答案是否定的,因為只有事務(wù)commit后才會更新到數(shù)據(jù)庫齐饮。
二. read committed(可以讀取其他事務(wù)提交的數(shù)據(jù))---大多數(shù)數(shù)據(jù)庫默認(rèn)的隔離級別
同樣的辦法,我們將用戶B所在的會話當(dāng)前事務(wù)隔離級別設(shè)置為read commited握恳。
在用戶A所在的會話中我們執(zhí)行下面操作:
update account set account=account-200 where id=1;
我們將id=1的用戶account減200乡洼。然后查詢,發(fā)現(xiàn)id=1的用戶account變?yōu)?00束昵。
在B用戶所在的會話中查詢:
select * from account葛峻;
結(jié)果如下:
我們會發(fā)現(xiàn)數(shù)據(jù)并沒有變术奖,還是1000。
接著在會話A中我們將事務(wù)提交:
commit;
在會話B中查詢結(jié)果如下:
結(jié)論二:
當(dāng)我們將當(dāng)前會話的隔離級別設(shè)置為read committed的時候佣耐,當(dāng)前會話只能讀取到其他事務(wù)提交的數(shù)據(jù)唧龄,未提交的數(shù)據(jù)讀不到。
那么這么做有什么問題嗎掖鱼?
那就是我們在會話B同一個事務(wù)中援制,讀取到兩次不同的結(jié)果。這就造成了不可重復(fù)讀晨仑,就是兩次讀取的結(jié)果不同拆檬。這種現(xiàn)象叫不可重復(fù)讀竟贯。
三. repeatable read(可重讀)---MySQL默認(rèn)的隔離級別
現(xiàn)在有個需求逝钥,就是老板說在同一個事務(wù)中查詢結(jié)果必須保持一致,如果你是數(shù)據(jù)庫持际,你會怎么做?數(shù)據(jù)庫是這么做的蜘欲。
在會話B中我們當(dāng)前事務(wù)隔離級別為repeatable read晌柬。具體操作如下:
set session transaction isolation level repeatable read;
start transaction;
接著在會話B中查詢數(shù)據(jù):
我們在A用戶所在會話中為表account添加一條數(shù)據(jù):
insert into account(id,account) value(3,1000);
commit;
然后我們查詢看數(shù)據(jù)插入是否成功:
回到B用戶所在的會話年碘,我們查詢結(jié)果:
用戶B在他所在的會話中想插入一條新數(shù)據(jù)id=3,value=1000闷祥。來我們操作下:
什么傲诵?竟然插不進(jìn)去,說我數(shù)據(jù)重復(fù)拴竹?
用戶B當(dāng)然不服啊,因為查詢到數(shù)據(jù)只有兩條啊座泳,為什么插入id=3說我數(shù)據(jù)重復(fù)了呢幕与?
我再看一遍,莫非我眼花了啦鸣?
試想一下,在實際中用戶A和用戶B肯定是相互隔離的香拉,彼此不知道操作什么。用戶B碰到這種現(xiàn)象凫碌,肯定會炸毛的啊,明明不存在的數(shù)據(jù)瞄摊,插入?yún)s說主鍵id=3數(shù)據(jù)重復(fù)了苦掘。
結(jié)論三:
當(dāng)我們將當(dāng)前會話的隔離級別設(shè)置為repeatable read的時候,當(dāng)前會話可以重復(fù)讀鸟蜡,就是每次讀取的結(jié)果集都相同揉忘,而不管其他事務(wù)有沒有提交端铛。
有什么問題嗎?
管他呢禾蚕,老板的要求滿足了。要一個事務(wù)中讀取的數(shù)據(jù)一致(可重復(fù)讀)哗总。我只能這么做啊倍试,打腫臉裝胖子。數(shù)據(jù)已經(jīng)發(fā)生改變县习,但是我還是要保持一致。但是叛本,出現(xiàn)了用戶B面對的問題,這種現(xiàn)象叫幻讀(記得當(dāng)時就在這個地方糾結(jié)好久来候,到底什么是幻讀耙荼ⅰ)性芬。
四. serializable(串行化)
同樣植锉,我們將用戶B所在的會話的事務(wù)隔離級別設(shè)置為serializable并開啟事務(wù)。
set session transaction isolation level serializable;
start transaction;
在用戶B所在的會話中我們執(zhí)行下面操作:
select * from account;
結(jié)果如下:
那我們這個時候在用戶A所在的會話中寫數(shù)據(jù)呢俊庇?
我們發(fā)現(xiàn)用戶A所在的會話陷入等待辉饱,如果超時(這個時間可以進(jìn)行配置)拣展,會出現(xiàn)Lock wait time out提示:
如果在等待期間我們用戶B所在的會話事務(wù)提交备埃,那么用戶A所在的事務(wù)的寫操作將提示操作成功。