事務(wù) 鎖 索引 日志文件 數(shù)據(jù)文件 各種buffer flush等概念的串起來
能夠解釋常見場(chǎng)景下 單個(gè)事務(wù)和多事務(wù)并發(fā)場(chǎng)景下,MySQL內(nèi)部處理機(jī)制
在MySQL中,按照鎖定的范圍胧洒,可以分為全局鎖、表級(jí)鎖、行級(jí)鎖嫁乘。
全局鎖
對(duì)MySQL進(jìn)程中的某個(gè)庫(kù)上鎖,上鎖的命令FLUSH TABLES WITH READ LOCK;
,這是當(dāng)前數(shù)據(jù)庫(kù)處于只讀狀態(tài)球碉,所有會(huì)改變數(shù)據(jù)蜓斧、表結(jié)構(gòu)等寫操作都會(huì)被阻塞。試驗(yàn)在只讀狀態(tài)下執(zhí)行insert操作睁冬,返回’Can't execute the query because you have a conflicting read lock‘挎春。(為什么是讀鎖沖突?)
全局鎖的使用場(chǎng)景是備份庫(kù),解鎖操作UNLOCK TABLES;
當(dāng)然備份過程中豆拨,肯定是要保證數(shù)據(jù)不會(huì)變更的直奋,不然會(huì)導(dǎo)致,課程中說的加入一個(gè)購(gòu)買流程辽装,包括添加課程和扣款兩個(gè)操作帮碰,如果不加全局鎖,在備份的時(shí)候就可能出現(xiàn)只會(huì)讀到其中一個(gè)操作的數(shù)據(jù)拾积,這就導(dǎo)致了數(shù)據(jù)錯(cuò)亂殉挽?
你可能會(huì)問,這兩個(gè)操作不應(yīng)該是在同一個(gè)事務(wù)中的嗎拓巧,如果只執(zhí)行了一個(gè)動(dòng)作斯碌,說明事務(wù)還未提交,你肯定還看不到更新呀肛度。這個(gè)邏輯我覺得有一定的道理傻唾,但假如這是由兩個(gè)服務(wù)操作的,也就是分布式事務(wù)的場(chǎng)景下承耿,當(dāng)前表的操作肯定先提交了冠骄。所以就能讀到了。
在備份數(shù)據(jù)時(shí)使用全局鎖加袋,不能再寫入了凛辣,那服務(wù)就得停擺,并且作為從庫(kù)時(shí)职烧,也不能及時(shí)同步主庫(kù)過來的binlog了扁誓。所以這種方式的成本很高防泵。基于這個(gè)原因蝗敢,我們可以選用另一種方案捷泞,將dump操作放在一個(gè)可重復(fù)讀的事務(wù)中,利用innodb的mvvc機(jī)制寿谴,就保證了不會(huì)讀到中間狀態(tài)的數(shù)據(jù)了锁右。
表級(jí)鎖
表級(jí)鎖有兩種,一是表鎖拭卿,二是元數(shù)據(jù)鎖(Meta Data Lock)骡湖。
表鎖的語(yǔ)法:lock tables xxx read;
或者lock tables xxx write;
。解鎖的語(yǔ)法unlock tables;
峻厚。
關(guān)于MDL响蕴,從級(jí)別來講也是表級(jí)的,鎖住的當(dāng)前標(biāo)的結(jié)構(gòu)定義惠桃,MDL是系統(tǒng)自動(dòng)添加和釋放的浦夷,DML也分讀和寫兩種。只有讀讀是共享的辜王,當(dāng)加了MDL鎖(讀或者寫)劈狐,DDL會(huì)被阻塞。
行鎖
行鎖是InnoDB特有的呐馆,能實(shí)現(xiàn)更細(xì)粒度的并發(fā)控制肥缔,這也是InnoDB取代MySQL原生存儲(chǔ)引擎MyISAM 的一個(gè)原因。在InnoDB事務(wù)中汹来,行鎖是在需要的時(shí)候才加上的续膳,但并不是不需要了就立刻釋 放,而是要等到事務(wù)結(jié)束時(shí)才釋放收班。這個(gè)就是兩階段鎖協(xié)議 坟岔。
關(guān)于鎖的問題,因?yàn)殒i的釋放是在事務(wù)結(jié)束后才會(huì)釋放的摔桦,如果一個(gè)事務(wù)獲得了鎖A社付,執(zhí)行完畢后,需要再次獲得鎖B邻耕,同時(shí)另一個(gè)事務(wù)鸥咖,已經(jīng)獲得了鎖B,執(zhí)行完后兄世,想要獲得鎖A啼辣。這時(shí)就會(huì)出現(xiàn)死鎖。
當(dāng)然死鎖對(duì)業(yè)務(wù)來說是無(wú)損的碘饼,因?yàn)镸ySQL定義了獲取鎖的超時(shí)時(shí)間熙兔,默認(rèn)是50s。如果超時(shí)艾恼,事務(wù)立即回滾住涉,對(duì)業(yè)務(wù)無(wú)損。但是這個(gè)時(shí)間太長(zhǎng)钠绍,并且也沒有什么依據(jù)來計(jì)算一個(gè)合理的超時(shí)時(shí)間舆声。所以在業(yè)務(wù)開發(fā)過程中,可以考慮將需要被頻繁獲取鎖的操作盡量放在事務(wù)的最后執(zhí)行柳爽,這樣可以盡量減少當(dāng)前事務(wù)對(duì)這個(gè)鎖的持有時(shí)間媳握。
另外一種方案,將熱點(diǎn)行分成多條數(shù)據(jù)磷脯。
問題:當(dāng)事務(wù)執(zhí)行更新操作時(shí)蛾找,MySQL到底選擇是行鎖還是表鎖。其實(shí)結(jié)合MySQL查找數(shù)據(jù)的機(jī)制來看赵誓,當(dāng)定位數(shù)據(jù)走了索引打毛,就會(huì)對(duì)這一行或多行添加行鎖,否則俩功,因?yàn)橹付ㄔ谥麈I索引中從頭遍歷幻枉,所以只能用表鎖。
所謂的行鎖诡蜓,本質(zhì)上在鎖什么 在鎖索引熬甫?
意向鎖 intention lock
“加鎖”操作,本質(zhì)上也會(huì)存在并發(fā)操作蔓罚,怎么保證一個(gè)鎖完整地被某個(gè)事務(wù)獲取呢椿肩,在redis中,setnx
間隙鎖 gap lock
next row lock
樂觀鎖與悲觀鎖
是在使用鎖控制資源競(jìng)爭(zhēng)的前提下脚粟,再次提高并發(fā)能力覆旱。
所謂的悲觀鎖,則認(rèn)為從”我“讀到我再寫的過程中核无,一定會(huì)有其他事務(wù)讀寫扣唱,所以從讀到寫整個(gè)過程我都加鎖。
所謂樂觀鎖团南,則認(rèn)為從”我“讀到我再寫的過程中噪沙,大部分情況下并沒有其他事務(wù)讀寫,所以讀的時(shí)候不再加鎖吐根,等寫的時(shí)候正歼,校驗(yàn)一把數(shù)據(jù)是否還是我當(dāng)時(shí)讀出來的樣子,如果是拷橘,則表示是真沒有其他事務(wù)在讀寫局义,那我就愉快滴把新數(shù)據(jù)寫入喜爷。這樣我的讀寫過程遍享受到了不加鎖的順滑。但假如在我寫的時(shí)候真有其他事務(wù)已經(jīng)謝過了萄唇,那我再重新讀一遍并重新計(jì)算要寫入的數(shù)據(jù)檩帐,一直重復(fù)這個(gè)過程,直至寫入數(shù)據(jù)成功另萤。
按照實(shí)際使用場(chǎng)景湃密,真正存在并發(fā)的時(shí)刻確實(shí)不是100%,所以樂觀鎖的優(yōu)勢(shì)就體現(xiàn)出來了四敞。
延伸:
悲觀鎖泛源、樂觀鎖其實(shí)根據(jù)實(shí)際場(chǎng)景提出的一種鎖優(yōu)化思想,這個(gè)思想在其他地方也有運(yùn)用忿危,比如Java中的自旋+CAS达箍,就是樂觀鎖的一種實(shí)現(xiàn)。比不管三七二十一铺厨,進(jìn)來先通過synchronized獲取鎖要高效多幻梯。本質(zhì)思想,盡可能減少不必要的加鎖操作