覺得自己對Mysql事務(wù)很熟悉婿禽?那可能是你沒碰到阿里面試官

前言

迎面走來了一個風塵仆仆的身穿格子衫的男子赏僧,手里拿著一個MacBook Pro大猛,看著那稀少的發(fā)量扭倾,和那從容淡定的眼神。

我心里一顫挽绩,我去膛壹,這是架構(gòu)師,架構(gòu)師來面我技術(shù)面唉堪,我心里頓時不淡定了模聋,表面很穩(wěn)實則心里慌得一批。

果然唠亚,他手里拿著我的簡歷链方,快速的掃了一下,然后用眼角余光看了一下我灶搜,上來就開問祟蚀。

Mysql事務(wù)簡介

面試官: 看你簡歷上說精通Mysql優(yōu)化方法,你先來說說你對Mysql的事務(wù)的了解吧割卖。

我心里喜了一下前酿,這個簡單啊,哥我可是北大(背大)的鹏溯,再來面試之前罢维,早就有準備的,二話不說丙挽,上去就是背肺孵。

我: 好的,數(shù)據(jù)庫的事務(wù)是指一組sql語句組成的數(shù)據(jù)庫邏輯處理單元颜阐,在這組的sql操作中悬槽,要么全部執(zhí)行成功,要么全部執(zhí)行失敗瞬浓。

我: 這里的一組sql操作初婆,舉個簡單又經(jīng)典的例子就是轉(zhuǎn)賬了,事務(wù)A中要進行轉(zhuǎn)賬猿棉,那么轉(zhuǎn)出的賬號要扣錢磅叛,轉(zhuǎn)入的賬號要加錢,這兩個操作都必須同時執(zhí)行成功萨赁,為了確保數(shù)據(jù)的一致性弊琴。

面試官: 剛才你提到了數(shù)據(jù)一致性,你知道事務(wù)的特性嗎杖爽?說說你的理解敲董。

ACID簡介

我: 在Mysql中事務(wù)的四大特性主要包含:原子性(Atomicity)紫皇、一致性(Consistent)隔離性(Isalotion)腋寨、持久性(Durable)聪铺,簡稱為ACID

我: 原子性是指事務(wù)的原子性操作萄窜,對數(shù)據(jù)的修改要么全部執(zhí)行成功铃剔,要么全部失敗,實現(xiàn)事務(wù)的原子性查刻,是基于日志的Redo/Undo機制键兜。

我: 一致性是指執(zhí)行事務(wù)前后的狀態(tài)要一致,可以理解為數(shù)據(jù)一致性穗泵。隔離性側(cè)重指事務(wù)之間相互隔離普气,不受影響,這個與事務(wù)設(shè)置的隔離級別有密切的關(guān)系佃延。

我: 持久性則是指在一個事務(wù)提交后现诀,這個事務(wù)的狀態(tài)會被持久化到數(shù)據(jù)庫中,也就是事務(wù)提交苇侵,對數(shù)據(jù)的新增赶盔、更新將會持久化到數(shù)據(jù)庫中。

我: 在我的理解中榆浓,原子性于未、隔離性、持久性都是為了保障一致性而存在的陡鹃,一致性也是最終的目的方妖。

心里暗自歡喜轩触,背完了县昂,平時背的多庇勃,面試就會說,幸好難不倒我脊阴。

ACID原理

面試官: 剛才你說原子性是基于日志的Redo/Undo機制握侧,你能說一說Redo/Undo機制嗎?

啊哈嘿期?我都說了什么品擎,不小心給自己埋了一顆大雷。不慌备徐,哥腦子里還有貨,假裝若有所思的停了幾十秒蜜猾,接著背振诬。

我: Redo/Undo機制比較簡單衍菱,它們將所有對數(shù)據(jù)的更新操作都寫到日志中赶么。

我: Redo log用來記錄某數(shù)據(jù)塊被修改后的值,可以用來恢復未寫入 data file 的已成功事務(wù)更新的數(shù)據(jù)梦碗;Undo log是用來記錄數(shù)據(jù)更新前的值蓖救,保證數(shù)據(jù)更新失敗能夠回滾。

我: 假如數(shù)據(jù)庫在執(zhí)行的過程中循捺,不小心崩了斩例,可以通過該日志的方式,回滾之前已經(jīng)執(zhí)行成功的操作念赶,實現(xiàn)事務(wù)的一致性恰力。

面試官: 可以舉一個場景,說一下具體的實現(xiàn)流程嗎踩萎?

我: 可以的香府,假如某個時刻數(shù)據(jù)庫崩潰董栽,在崩潰之前有事務(wù)A和事務(wù)B在執(zhí)行锭碳,事務(wù)A已經(jīng)提交勿璃,而事務(wù)B還未提交。當數(shù)據(jù)庫重啟進行 crash-recovery 時补疑,就會通過Redo log將已經(jīng)提交事務(wù)的更改寫到數(shù)據(jù)文件癣丧,而還沒有提交的就通過Undo log進行roll back。

事務(wù)隔離級別

面試官: 之前你還提到事務(wù)的隔離級別胁编,你能說一說嗎?

我: 可以的早直,在Mysql中事務(wù)的隔離級別分為四大等級,讀未提交(READ UNCOMMITTED)霞扬、讀提交 (READ COMMITTED)糕韧、可重復讀 (REPEATABLE READ)喻圃、串行化 (SERIALIZABLE)

我: 讀未提交會讀到另一個事務(wù)的未提交的數(shù)據(jù)雀扶,產(chǎn)生臟讀問題肆汹,讀提交則解決了臟讀的,出現(xiàn)了不可重復讀浪册,即在一個事務(wù)任意時刻讀到的數(shù)據(jù)可能不一樣岗照,可能會受到其它事務(wù)對數(shù)據(jù)修改提交后的影響,一般是對于update的操作煞肾。

我: 可重復讀解決了之前不可重復讀和臟讀的問題嗓袱,但是由帶來了幻讀的問題渠抹,幻讀一般是針對inser操作。

我: 例如:第一個事務(wù)查詢一個User表id=100發(fā)現(xiàn)不存在該數(shù)據(jù)行奇颠,這時第二個事務(wù)又進來了放航,新增了一條id=100的數(shù)據(jù)行并且提交了事務(wù)。

我: 這時第一個事務(wù)新增一條id=100的數(shù)據(jù)行會報主鍵沖突荆几,第一個事務(wù)再select一下,發(fā)現(xiàn)id=100數(shù)據(jù)行已經(jīng)存在行拢,這就是幻讀诞吱。

面試官: 小伙子你能演示一下嗎?我不太會你能教教我嗎沼瘫?我電腦在這里握巢,你演示我看一看松却。

男人的嘴騙人的鬼晓锻,我信你個鬼,你這糟老頭子壞得很砚哆,出來裝X總是要還的躁锁,只能默默含淚把它敲完。

我: 首先創(chuàng)建一個User表搜立,最為一個測試表槐秧,測試表里面有三個字段,并插入兩條測試數(shù)據(jù)颠通。

CREATE TABLE User (
  id INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(20),
  age INT   DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=gb2312;

INSERT INTO `user` VALUES (1, 'zhangsan', 23);
INSERT INTO `user` VALUES (2, 'lisi', 20);

我: 在Mysql中可以先查詢一下他的默認隔離級別膀懈,可以看出Mysql的默認隔離級別是REPEATABLE-READ

image

我: 先來演示一下讀未提交乘客,先把默認的隔離級別修改為READ UNCOMMITTED淀歇。

image

我: 他設(shè)置隔離級別的語句中set global transaction isolation level read uncommitted浪默,這里的global也可以換成session,global表示全局的纳决,而session表示當前會話阔加,也就是當前窗口有效。

我: 當設(shè)置完隔離級別后對于之前打開的會話胳喷,是無效的夭织,要重新打開一個窗口設(shè)置隔離級別才生效。

image.png

我: 然后是開啟事務(wù)讲竿,Mysql中開啟事務(wù)有兩種方式begin/start transaction题禀,最后提交事務(wù)執(zhí)行commit膀捷,或者回滾事務(wù)rollback。

我: 在執(zhí)行begin/start transaction命令江锨,它們并不是一個事務(wù)的起點糕篇,在執(zhí)行完它們后的第一個sql語句,才表示事務(wù)真正的啟動 挑豌。

我: 這里直接打開兩個新的窗口,同時開啟事務(wù)侯勉,在第一個窗口先update一個id=1的數(shù)據(jù)行name改為'非科班的科班'铝阐,執(zhí)行成功徘键。

image

我: 然后再第二個窗口執(zhí)行兩次的查詢,分別是窗口一update之前的查詢和update之后的查詢螟凭。

image

我: 第一個session產(chǎn)生的未提交的事務(wù)的狀態(tài)就會直接影響到第二sesison螺男,也就是臟讀纵穿。

我: 對于讀提交也是一樣的,開啟事務(wù)后汪拥,第一個事務(wù)先執(zhí)行查詢數(shù)據(jù)篙耗,然后第二個session執(zhí)行update操作宪赶,但是還沒有commit搂妻,這是第一個session再次select,數(shù)據(jù)并沒有改變邓厕,再第二個session執(zhí)行commit之后扁瓢,第一個session再次select就是改變后的數(shù)據(jù)了。

image

我: 這樣第一個事務(wù)的查詢結(jié)果就會收到第二事務(wù)的影響昧互,這個也就是產(chǎn)生不可重復讀的問題敞掘。

面試官: 小伙子你能畫一下他執(zhí)行的過程圖嗎?你講的我有點亂更扁,我還沒有徹底明白赫冬。

我心里一萬只什么馬在飛過,欲哭無淚竖哩,這面試官真難伺候脊僚,說時遲那時快,從左屁股兜抽出筆增淹,從右屁股兜拿出紙乌企,開始畫加酵。

image

我: 這個是讀提交的時間軸圖,讀未提交的時間軸圖冗澈,原理也一樣的陋葡,第二個select的時候數(shù)據(jù)就已經(jīng)改變了腐缤。

這是面試官拿過我的圖看了一點,微微的點了點頭岭粤,嘴角露出思思的笑意绍在,我想你這糟老頭子應(yīng)該不會再刁難我了吧雹有。

面試官: 嗯臼寄,你接著演示你的可重復讀吧吉拳。

我: 嗯,好的煤惩,然后就是可重復讀炼邀,和之前一樣的操作。

image

我: 將兩個session開啟為REPEATABLE READ洛退,同時開啟事務(wù)兵怯,在第一個事務(wù)中先select腔剂,然后在第二個事務(wù)里面update數(shù)據(jù)行,可以發(fā)現(xiàn)即使第二個事務(wù)已經(jīng)commit袜漩,第一個事務(wù)再次select數(shù)據(jù)也還是沒有改變登渣,這就解決了不可重復讀的問題胜茧。

我: 這里有個不同的地方就是在Mysql中仇味,默認的不可重復讀個隔離級別也解決了幻讀的問題。

我: 從上面的演示中可以看出第一個事務(wù)中先select一個id=3的數(shù)據(jù)行廊遍,這條數(shù)據(jù)行是不存在的贩挣,返回Empty set,然后第二個事務(wù)中insert一條id=3的數(shù)據(jù)行并且commit卵迂,第一個事務(wù)中再次select的见咒,數(shù)據(jù)也好是沒有id=3的數(shù)據(jù)行。

我: 最后的串行化下翎,樣式步驟也是一樣的宝当,結(jié)果也和Mysql中默認的個可重復讀隔離級別的結(jié)果一樣,串行化的執(zhí)行流程相當于把事務(wù)的執(zhí)行過程變?yōu)轫樞驁?zhí)行俐东,我這邊就不再做演示了犬性。

我: 這四大等級從上到下腾仅,隔離的效果是逐漸增強,但是性能卻是越來越差鹤耍。

Mysql的鎖機制

面試官: 哦验辞?性能越來越差跌造?為什么會性能越來越差?你能說一說原因嗎陵珍?

哎呀违施,我這嘴,少說一句會死啊留潦,這下好了,這個得說底層實現(xiàn)原理了殖卑,從原來的假裝若有所思秆乳,變成了真正得若有所思屹堰。

我: 這個得從Mysq的鎖說起,在Mysql中的鎖可以分為分享鎖/讀鎖(Shared Locks)睦袖、排他鎖/寫鎖(Exclusive Locks) 荣刑、間隙鎖行鎖(Record Locks)厉亏、表鎖爱只。

我: 在四個隔離級別中加鎖肯定是要消耗性能的,而讀未提交是沒有加任何鎖的窝趣,所以對于它來說也就是沒有隔離的效果训柴,所以它的性能也是最好的幻馁。

我: 對于串行化加的是一把大鎖,讀的時候加共享鎖仗嗦,不能寫儒将,寫的時候对蒲,加的是排它鎖,阻塞其它事務(wù)的寫入和讀取砰逻,若是其它的事務(wù)長時間不能寫入就會直接報超時蝠咆,所以它的性能也是最差的,對于它來就沒有什么并發(fā)性可言闸翅。

我: 對于讀提交和可重復讀菊霜,他們倆的實現(xiàn)是兼顧解決數(shù)據(jù)問題鉴逞,然后又要有一定的并發(fā)行,所以在實現(xiàn)上鎖機制會比串行化優(yōu)化很多液南,提高并發(fā)性勾徽,所以性能也會比較好捂蕴。

事務(wù)底層實現(xiàn)原理

我: 他們倆的底層實現(xiàn)采用的是MVCC(多版本并發(fā)控制)方式進行實現(xiàn)。

面試官: 你能先說一下先這幾個鎖的概念嗎涡匀?我不是很懂溉知,說說你的理解级乍。

我: 哦,好的甚淡,共享鎖是針對同一份數(shù)據(jù)捅厂,多個讀操作可以同時進行,簡單來說即讀加鎖贿堰,不能寫并且可并行讀啡彬;排他鎖針對寫操作,假如當前寫操作沒有完成纵搁,那么它會阻斷其它的寫鎖和讀鎖诡渴,即寫加鎖菲语,其它讀寫都阻塞 。

我: 而行鎖和表鎖眼耀,是從鎖的粒度上進行劃分的哮伟,行鎖鎖定當前數(shù)據(jù)行妄帘,鎖的粒度小,加鎖慢鬼廓,發(fā)生鎖沖突的概率小碎税,并發(fā)度高馏锡,行鎖也是MyISAM和InnoDB的區(qū)別之一杯道,InnoDB支持行鎖并且支持事務(wù) 。

我: 而表鎖則鎖的粒度大虐杯,加鎖快,開銷小,但是鎖沖突的概率大达舒,并發(fā)度低叹侄。

我: 間隙鎖則分為兩種:Gap LocksNext-Key Locks趾代。Gap Locks會鎖住兩個索引之間的區(qū)間,比如select * from User where id>3 and id<5 for update禽捆,就會在區(qū)間(3飘哨,5)之間加上Gap Locks芽隆。

我: Next-Key Locks是Gap Locks+Record Locks形成閉區(qū)間鎖select * from User where id>=3 and id=<5 for update胚吁,就會在區(qū)間[3,5]之間加上Next-Key Locks。

面試官: 那Mysql中什么時候會加鎖呢孽拷?

我: 在數(shù)據(jù)庫的增蕉毯、刪乓搬、改、查中代虾,只有增进肯、刪、改才會加上排它鎖棉磨,而只是查詢并不會加鎖江掩,只能通過在select語句后顯式加lock in share mode或者for update來加共享鎖或者排它鎖。

面試官: 你在上面提到MVCC(多版本并發(fā)控制),你能說一說原理嗎环形?

我: 在實現(xiàn)MVCC時用到了一致性視圖策泣,用于支持讀提交和可重復讀的實現(xiàn)抬吟。

我: 在實現(xiàn)可重復讀的隔離級別萨咕,只需要在事務(wù)開始的時候創(chuàng)建一致性視圖,也叫做快照火本,之后的查詢里都共用這個一致性視圖危队,后續(xù)的事務(wù)對數(shù)據(jù)的更改是對當前事務(wù)是不可見的,這樣就實現(xiàn)了可重復讀钙畔。

我: 而讀提交茫陆,每一個語句執(zhí)行前都會重新計算出一個新的視圖,這個也是可重復讀和讀提交在MVCC實現(xiàn)層面上的區(qū)別擎析。

面試官: 那你知道快照(視圖)在MVCC底層是怎么工作的嗎簿盅?

我: 在InnoDB 中每一個事務(wù)都有一個自己的事務(wù)id,并且是唯一的揍魂,遞增的 桨醋。

我: 對于Mysql中的每一個數(shù)據(jù)行都有可能存在多個版本,在每次事務(wù)更新數(shù)據(jù)的時候愉烙,都會生成一個新的數(shù)據(jù)版本讨盒,并且把自己的數(shù)據(jù)id賦值給當前版本的row trx_id。

面試官: 小伙子你可以畫個圖我看看嗎步责?我不是很明白返顺。

我有什么辦法呢?完全沒辦法蔓肯,只能又從屁股兜里拿出筆和紙遂鹊,迅速的畫了起來,要是這次面試不過就血虧啊蔗包,浪費了我兩張紙和筆水秉扑,現(xiàn)在的筆和紙多貴啊,只能豁出去了调限。

image

我: 如圖中所示舟陆,假如三個事務(wù)更新了同一行數(shù)據(jù),那么就會有對應(yīng)的三個數(shù)據(jù)版本耻矮。

我: 實際上版本1秦躯、版本2并非實際物理存在的,而圖中的U1和U2實際就是undo log裆装,這v1和v2版本是根據(jù)當前v3和undo log計算出來的踱承。

面試官: 那對于一個快照來說倡缠,你知道它要遵循什么規(guī)則嗎?

我: 嗯茎活,對于一個事務(wù)視圖來說除了對自己更新的總是可見昙沦,另外還有三種情況:版本未提交的,都是不可見的载荔;版本已經(jīng)提交盾饮,但是是在創(chuàng)建視圖之后提交的也是不可見的;版本已經(jīng)提交身辨,若是在創(chuàng)建視圖之前提交的是可見的丐谋。

面試官: 假如兩個事務(wù)執(zhí)行寫操作,又怎么保證并發(fā)呢煌珊?

我: 假如事務(wù)1和事務(wù)2都要執(zhí)行update操作,事務(wù)1先update數(shù)據(jù)行的時候泌豆,先回獲取行鎖定庵,鎖定數(shù)據(jù),當事務(wù)2要進行update操作的時候踪危,也會取獲取該數(shù)據(jù)行的行鎖蔬浙,但是已經(jīng)被事務(wù)1占有,事務(wù)2只能wait贞远。

我: 若是事務(wù)1長時間沒有釋放鎖畴博,事務(wù)2就會出現(xiàn)超時異常 。

面試官: 這個是在update的where后的條件是在有索引的情況下吧蓝仲?

我: 嗯俱病,是的 。

面試官: 那沒有索引的條件下呢袱结?沒辦法快速定位到數(shù)據(jù)行呢亮隙?

我: 若是沒有索引的條件下,就獲取所有行垢夹,都加上行鎖溢吻,然后Mysql會再次過濾符合條件的的行并釋放鎖,只有符合條件的行才會繼續(xù)持有鎖果元。

我: 這樣的性能消耗也會比較大促王。

面試官: 嗯嗯

此時面試官看看手表一個多鐘已經(jīng)過去了,也已經(jīng)到了飯點時刻而晒,我想他應(yīng)該是肚子餓了蝇狼,不會繼續(xù)追問吧,兩人持續(xù)僵了三十秒欣硼,他終于開口了题翰。

面試官: 小伙子恶阴,現(xiàn)在時間也已經(jīng)到了飯點了,今天的面試就到此結(jié)束吧豹障,你回去等通知吧冯事。

我: 。血公。昵仅。。累魔。摔笤。。垦写。吕世。。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末梯投,一起剝皮案震驚了整個濱河市命辖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌分蓖,老刑警劉巖尔艇,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異么鹤,居然都是意外死亡终娃,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進店門蒸甜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棠耕,“玉大人,你說我怎么就攤上這事迅皇∶亮桑” “怎么了?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵登颓,是天一觀的道長搅荞。 經(jīng)常有香客問我,道長框咙,這世上最難降的妖魔是什么咕痛? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮喇嘱,結(jié)果婚禮上茉贡,老公的妹妹穿的比我還像新娘。我一直安慰自己者铜,他們只是感情好腔丧,可當我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布放椰。 她就那樣靜靜地躺著,像睡著了一般愉粤。 火紅的嫁衣襯著肌膚如雪砾医。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天衣厘,我揣著相機與錄音如蚜,去河邊找鬼。 笑死影暴,一個胖子當著我的面吹牛错邦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播型宙,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼撬呢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了妆兑?” 一聲冷哼從身側(cè)響起倾芝,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎箭跳,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體潭千,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡谱姓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了刨晴。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屉来。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖狈癞,靈堂內(nèi)的尸體忽然破棺而出茄靠,到底是詐尸還是另有隱情,我是刑警寧澤蝶桶,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布慨绳,位于F島的核電站,受9級特大地震影響真竖,放射性物質(zhì)發(fā)生泄漏脐雪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一恢共、第九天 我趴在偏房一處隱蔽的房頂上張望战秋。 院中可真熱鬧,春花似錦讨韭、人聲如沸脂信。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狰闪。三九已至疯搅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尝哆,已是汗流浹背秉撇。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留秋泄,地道東北人琐馆。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像恒序,于是被迫代替她去往敵國和親瘦麸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,576評論 2 349