原文:MySQL系統(tǒng)學習(05):事務(wù)隔離性與隔離級別
數(shù)據(jù)庫事務(wù)
提到事務(wù),大家都不陌生柠逞。在剛開始學習數(shù)據(jù)庫的時候昧狮,都接觸多轉(zhuǎn)賬的例子。這個過程能夠準確板壮、順利的執(zhí)行靠的是數(shù)據(jù)庫的事務(wù)保證的逗鸣。
簡單來說,事務(wù)是保證對數(shù)據(jù)庫的一組操作绰精,要么全部成功撒璧、要么全部失敗。在MySQL中事務(wù)支持也是在存儲引擎層實現(xiàn)的笨使。第一篇文章中學習到數(shù)據(jù)庫的存儲引擎是插件式的卿樱,可擴展可切換的,即MYSQL是一個支持多種存儲引擎的系統(tǒng)硫椰。但是殿如,并不是所有的存儲引擎都支持事務(wù)。 比如MySQL原生的MyISAM引擎就不支持事務(wù)最爬,這也是MyISAM被InnoDB取代的重要原因之一涉馁。
這篇文章,還是以InnoDB為例爱致,剖析MySQL在事務(wù)支持方面的特定實現(xiàn)烤送,并給予原理給出相應(yīng)的實踐建議。
事務(wù)的隔離性與隔離級別
隔離性與隔離級別是事務(wù)學習的核心糠悯,也是面試中經(jīng)常被問到的帮坚。
提到事務(wù)我們會先想到事務(wù)的4大特性:ACID(原子性、一致性互艾、隔離性與持久性)试和,這篇文章我們主要來看下其中的I,即隔離性纫普。
而當多個事務(wù)同時執(zhí)行的時候阅悍,可能出現(xiàn)“臟讀”、“幻讀/虛讀”、”不可重復(fù)讀“的問題节视。為了避免這些問題拳锚,就有了”隔離級別“的概念。
在談隔離級別之前寻行,我們先需要明確一個問題霍掺,隔離的越嚴實,必然會影響效率拌蜘。因此很多時候我們都需要在二者之間尋找一個平衡點杆烁。
SQL標準的隔離級別包括:讀未提交(read uncommitted)、讀提交(read committed)简卧、可重復(fù)讀(repeatable read)和串行化(serializable)连躏。
1.讀未提交 指一個事務(wù)還沒提交時,它的變更就能被其他事務(wù)看到
2.讀提交 與提未提交相反贞滨,指一個事務(wù)提交之后,他的變更才能被其他事務(wù)看到
3.可重復(fù)度指一個事務(wù)在執(zhí)行過程中看到的數(shù)據(jù)拍棕,總是跟這個事務(wù)在啟動時看到的數(shù)據(jù)是一致的晓铆。 當然在可重復(fù)讀隔離級別下,未提交變更對其他事務(wù)也是不可見
4.串行化對于同一行數(shù)據(jù)绰播,”寫“會加”寫鎖“骄噪,”讀“會加”讀鎖“。當存在讀與寫兩個事務(wù)沖突時蠢箩,后訪問的事務(wù)必須等前一個事務(wù)執(zhí)行完链蕊,才能繼續(xù)執(zhí)行。
其中谬泌,讀提交和可重復(fù)相對難理解一些滔韵。所以準備了一個例子,有一張表T掌实,里面只有一列c
-- 創(chuàng)建表T
CREATE TABLE T (c int) engine=INNODB;
-- 插入一行數(shù)據(jù)
INSERT INTO T (c) VALUES (1);
現(xiàn)在有兩個事務(wù)A陪蜻、事務(wù)B是做了這樣的事情,在不同的隔離級別下v1贱鼻,v2宴卖,v3的結(jié)果是什么:
1.若當前隔離級別設(shè)置的是”讀未提交“,則v1的值就是2邻悬。這是雖然B還沒提交症昏,但是根據(jù)讀未提交的規(guī)則定義來看,A是這是已經(jīng)可以看到B事務(wù)的當前執(zhí)行結(jié)果了父丰。V2, v3自然也都是2.
2.若是”讀提交“肝谭,則V1是1 ,因為B的事務(wù)在提交后,A便可以看得到了分苇,所以v2添诉、v3是2
3.若是”可重復(fù)讀“,根據(jù)定義医寿,A執(zhí)行過程中看到的數(shù)據(jù)總是跟事務(wù)啟動時看到的一致栏赴,那說明在A提交之前讀取的v1,v2都是1. V3是2.
4.若是”串行化“靖秩,事務(wù)B在執(zhí)行”1改為2“這個寫操作的時候會被鎖住须眷。直到A事務(wù)提交才可以繼續(xù)執(zhí)行,所以v1沟突,v2是1花颗,V3是2
在實現(xiàn)上數(shù)據(jù)庫會創(chuàng)建一個視圖,訪問的時候以數(shù)據(jù)庫視圖為準惠拭。
在”可重復(fù)讀“的隔離界別下扩劝,這個視圖是在事務(wù)啟動時創(chuàng)建的,整個事務(wù)存在期間都用這個視圖职辅。
在”讀提交“隔離級別下棒呛,這個視圖是在SQL語句開始執(zhí)行的時候創(chuàng)建的。
在”讀未提交“隔離級別下域携,直接返回記錄中的最新值簇秒,沒有視圖概念。
在”串行化“隔離級別下秀鞭,直接通過加鎖避免并行訪問趋观。
我們可以看到不同隔離級別下,數(shù)據(jù)庫行為是有所不同的锋边。Oracle數(shù)據(jù)庫的默認給級別是“讀提交”皱坛,因此對于一些從Oracle遷移到MySQL的應(yīng)用,我們一定要記得將MySQL的隔離級別也設(shè)置為”讀提交“豆巨,不然會發(fā)生因隔離性引起的并發(fā)訪問數(shù)據(jù)庫問題麸恍。
配置方式,將啟動參數(shù)transaction-isolation的值設(shè)置為READ-COMMITTED搀矫∧ɑΓ可以用show variables查看當前值。
mysql> show variables like 'transaction_isolation'
事務(wù)隔離的實現(xiàn)
理解了事務(wù)的隔離級別瓤球,再來看看事務(wù)的隔離級別具體是怎么實現(xiàn)的融欧。這里我們展開說明”可重復(fù)讀“。
在MySQL中卦羡,實際每一條日志在更新的時候噪馏,都會記錄一條對應(yīng)的回滾日志麦到。記錄上最新值,通過回滾可以得到前一個狀態(tài)的值欠肾。
當前值為4瓶颠,但是當讀取這條記錄的時候,不同時刻啟動的事務(wù)會有不同的視圖刺桃。如圖中所示粹淋,在視圖A、B瑟慈、C里面對應(yīng)的記錄值分別為:1,2,4桃移,同一條記錄再數(shù)據(jù)庫中可以存多個版本,就是由數(shù)據(jù)的多版本并發(fā)控制葛碧。對于視圖ReadView1要想得到值1借杰,就必須按圖中的順序依次執(zhí)行回滾操作。
同時我們會發(fā)現(xiàn)进泼,即使現(xiàn)在有另外一個事務(wù)正在對記錄做變更蔗衡,將值改為5,這個事務(wù)跟readviewA,B,C是不會沖突的乳绕。
回滾日志總不能一直保留吧绞惦?什么時候刪除?答案是在不需要的時候刪除刷袍。
事務(wù)的啟動方式
MySQL啟動事務(wù)的方式有以下幾種:
1.顯示啟動事務(wù)語句, begin或者start transaction樊展。配套的提交語句是commit呻纹,回滾語句是rollback
2.set autocommit=0,這個命令會將這個線程的自動提交關(guān)閉专缠。以為著我們只執(zhí)行一個select雷酪,這個事務(wù)就啟動了,而且并不會自動提交涝婉。這個事務(wù)持續(xù)存在哥力,除非我們主動commit或者rollback,或者斷開連接墩弯。
有些客戶端連接框架會默認連接成功后先執(zhí)行set autocommit=0吩跋。這樣導(dǎo)致接下來的查詢都在事務(wù)中,如果是長連接渔工,就導(dǎo)致了意外的長事務(wù)锌钮。
所以建議我們總是使用set autocommit=1,聽過語句顯示的方式啟動事務(wù)引矩。(commit work and chain這個命令表示提交一個事務(wù)梁丘,并自動啟動下一個事務(wù))
我們可以在information_schema庫的innodb_trx這個表中查詢長事務(wù)侵浸。比如下面這個語句可以查找持續(xù)時間超過60s的事務(wù):
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(), trx_started)) > 60
概念補充
關(guān)于ACID的解釋:
原子性
:一個事務(wù)可能有多個不同的操作(事務(wù)就是一系列的操作),那它也是一個不可分割的單元氛谜,要么全執(zhí)行成功掏觉,要么全執(zhí)行失敗。
一致性
:事務(wù)執(zhí)行的前后都是合法的數(shù)據(jù)狀態(tài)值漫,不會違背任何的數(shù)據(jù)完整性澳腹,這就是“一致”的意思。
隔離性
:多個事務(wù)執(zhí)行惭嚣,他們互不干擾遵湖。
持久性
:事務(wù)一旦提交執(zhí)行成功,那結(jié)果是不可改變的晚吞。(事務(wù)一旦被提交延旧,那么數(shù)據(jù)一定會被寫入到數(shù)據(jù)庫中并持久儲存起來。)
個人博客網(wǎng)站:RelaxHeart網(wǎng)-Tec博客