1息堂、為什么要有數(shù)據(jù)庫(kù)的鎖機(jī)制?
????????為了解決數(shù)據(jù)庫(kù)層面的并發(fā)問(wèn)題块促,多個(gè)事務(wù)同時(shí)修改一份數(shù)據(jù)資源荣堰,可能導(dǎo)致臟讀、不可重復(fù)讀竭翠、幻讀的問(wèn)題振坚。
2、讀鎖和寫(xiě)鎖
????????數(shù)據(jù)庫(kù)的鎖都分為讀鎖和寫(xiě)鎖斋扰,讀鎖是共享鎖(S鎖)渡八,各個(gè)讀鎖之間是共享數(shù)據(jù)的;寫(xiě)鎖是排他鎖(X鎖)褥实,加了一把寫(xiě)鎖呀狼,對(duì)于這個(gè)數(shù)據(jù)就不能加其它寫(xiě)鎖了。
3损离、全局鎖
????????使用flush tables with read lock(FTWRL)哥艇,可以鎖住整庫(kù),一般用來(lái)做全庫(kù)的備庫(kù)僻澎。
????????一般我們不會(huì)在主庫(kù)上做備份貌踏,而是在備庫(kù)上做備份十饥。
4、表鎖
????????鎖住整個(gè)表的數(shù)據(jù)祖乳,Lock tables逗堵,一般不用。
5眷昆、MDL鎖
????????元數(shù)據(jù)鎖蜒秤,分為MDL讀鎖和MDL寫(xiě)鎖,這兩個(gè)鎖都是自動(dòng)加上的亚斋,加的時(shí)機(jī)如下:
? ? ? ? 1)對(duì)表進(jìn)行增刪改查的時(shí)候作媚,會(huì)自動(dòng)加MDL讀鎖,表示只能讀取表的元數(shù)據(jù)帅刊,不能改表結(jié)構(gòu)纸泡;
? ? ? ? 2)對(duì)表修改表結(jié)構(gòu)的時(shí)候,會(huì)自動(dòng)加MDL寫(xiě)鎖赖瞒,表示這邊在改表結(jié)構(gòu)女揭,不能再有其它的事務(wù)改表結(jié)構(gòu)了。
????????MDL鎖都是在事務(wù)提交以后釋放的栏饮,所以如果我們需要在線上環(huán)境給一個(gè)表加字段吧兔,那么就不能有長(zhǎng)事務(wù)正在執(zhí)行,要不然MDL鎖會(huì)一起不釋放抡爹,影響其它事務(wù)的運(yùn)行掩驱。
????????我們通過(guò)innodb_trx表可以查出來(lái)長(zhǎng)事務(wù)芒划。
6冬竟、online DDL
????????Online DDL是Mysql5.6以后提供的一種支持在數(shù)據(jù)庫(kù)數(shù)據(jù)操作執(zhí)行過(guò)程當(dāng)中,執(zhí)行DDL操作民逼,而不完全阻塞當(dāng)前數(shù)據(jù)庫(kù)數(shù)據(jù)操作的機(jī)制泵殴。
????????以后沒(méi)有online DDL,每次DDL操作拼苍,都會(huì)通過(guò)MDL鎖鎖住全表笑诅,等鎖釋放以后,才能繼續(xù)執(zhí)行數(shù)據(jù)庫(kù)的DML操作疮鲫。有了online DDL以后吆你,就簡(jiǎn)單很多了,不會(huì)影響DML操作俊犯,但是注意妇多,還是會(huì)影響一點(diǎn),因?yàn)閛nline DDL過(guò)程還是加了鎖燕侠,只是加鎖過(guò)程較短者祖,具體過(guò)程如下:
? ? ? ? 1)加MDL x鎖立莉,直接鎖表。這一階段七问,會(huì)根據(jù)原表創(chuàng)建臨時(shí)表蜓耻;
? ? ? ? 2)降級(jí)為MDL s鎖,此時(shí)DML操作可執(zhí)行械巡。這一階段刹淌,會(huì)把原表的數(shù)據(jù)一行一行copy到臨時(shí)表中;
? ? ? ? 3)升級(jí)為MDL x鎖讥耗,直接鎖表芦鳍。這一階段,會(huì)啟用臨時(shí)表葛账。
7柠衅、行鎖
7.1? ??二階段鎖
????????首先,行鎖都是二階段鎖籍琳,二階段表示鎖的加鎖階段與解鎖階段菲宴,一個(gè)事務(wù)當(dāng)中,每次執(zhí)行如下語(yǔ)句的時(shí)候趋急,都會(huì)加鎖:
????????Update/delete xxxxxx where xxxx(X鎖)
????????Select ?....... ?where xxxx ?lock in share mode(S鎖)
????????Select ?....... ?where xxxx ?for update(X鎖)
????????解鎖只有在事務(wù)提交的時(shí)候執(zhí)行喝峦。
????????所以,一個(gè)事務(wù)當(dāng)中的不同語(yǔ)句加的鎖的時(shí)機(jī)是不一樣的呜达,有早有晚谣蠢,所以我們應(yīng)當(dāng)盡量把最可能造成鎖沖突、最可能影響并發(fā)度的鎖放在后面查近,這樣這把鎖的加鎖時(shí)間就短了眉踱。
7.2? ??行鎖升級(jí)表鎖
????????由于行鎖其實(shí)是鎖的索引,所以所有全表掃描的行鎖語(yǔ)句霜威,都會(huì)自動(dòng)升級(jí)為表鎖谈喳,會(huì)導(dǎo)致全表掃描的情況包括:
? ? ? ? ——檢索沒(méi)加索引;
? ? ? ? ——索引上有隱式類型轉(zhuǎn)換戈泼;
? ? ? ? ——用了二級(jí)索引婿禽,但是二級(jí)索引取的數(shù)據(jù)占全部數(shù)據(jù)的30%左右,優(yōu)化器就會(huì)覺(jué)得還不如走全表掃描算了大猛,效率可能更高扭倾。
7.3? ??鎖沖突
????????一條SQL語(yǔ)句加鎖之后,就會(huì)影響其余加鎖語(yǔ)句的執(zhí)行挽绩,鎖與鎖之間的影響是:
? ? ? ? ——通過(guò)lock in share mode加讀鎖后膛壹,不影響其它加讀鎖SQL的執(zhí)行,但會(huì)阻塞其它加寫(xiě)鎖SQL的執(zhí)行琼牧;
? ? ? ? ——通過(guò)update或for update加寫(xiě)鎖后恢筝,會(huì)影響其它加讀鎖和加寫(xiě)鎖SQL的執(zhí)行哀卫。
7.4? ??Next-key lock
????????InnoDB給SQL語(yǔ)句加的鎖是Next-key lock,而不是行鎖撬槽,next-key lock包含了行鎖和間隙鎖此改,SQL語(yǔ)句加的鎖有可能是行鎖,也有可能是間隙鎖侄柔,也有可能是兩種都有共啃。
?????? 1)對(duì)于唯一索引的等值查詢加鎖,分兩種情況:
????????●? 能查到值暂题,那么next-key lock會(huì)退化成行鎖移剪;
????????●? 查不到值,那么next-key lock會(huì)退化成間隙鎖薪者。
? ? ? ? 2)對(duì)于普通索引的等值查詢加鎖纵苛,分兩種情況:
????????●? 加s鎖,并且查詢是覆蓋索引言津,那么加next-key lock攻人,由于普通索引不是唯一索引,所以會(huì)繼續(xù)往下找悬槽,因此又會(huì)再加一個(gè)next-key lock怀吻,并且會(huì)退化成間隙鎖。并且鎖只會(huì)在普通索引的B+樹(shù)上初婆,不會(huì)對(duì)主鍵索引的B+樹(shù)上鎖蓬坡,因此不影響主健索引上的查詢操作;
????????●? 加X(jué)鎖磅叛,不管是不是覆蓋索引屑咳,next-key lock會(huì)退化成間隙鎖。
? ? ? ? 3)對(duì)于主健索引的范圍查詢加鎖宪躯,會(huì)根據(jù)范圍加next-key lock乔宿,注意位迂,范圍查詢不會(huì)退化成行鎖或者間隙鎖访雪,還須注意,如果范圍正好卡在next-key lock的邊界上掂林,那么會(huì)繼續(xù)往下一個(gè)next-key lock找不滿足要求的值臣缀,所以會(huì)在原來(lái)的基礎(chǔ)上又多了一個(gè)next-key lock;
? ? ? ? 4)對(duì)于普通索引的范圍查詢加鎖泻帮,會(huì)根據(jù)范圍加next-key lock精置,由于范圍查詢不會(huì)退化成行鎖或者間隙鎖,因此最后的鎖就是next-key lock锣杂。
? ? ? ? 5)最后脂倦,間隙鎖是共享鎖番宁,不同事務(wù)對(duì)于同一個(gè)間隙索是兼容的,都可以加鎖赖阻,不會(huì)阻塞蝶押。而行鎖的讀鎖是共享鎖,寫(xiě)鎖是排他鎖火欧。間隙鎖是共享鎖會(huì)帶來(lái)一個(gè)問(wèn)題棋电,就是兩個(gè)事務(wù)都申請(qǐng)到了同一個(gè)間隙鎖,那么它們對(duì)這個(gè)間隙進(jìn)行insert操作苇侵,就會(huì)死鎖赶盔,被各自的間隙鎖給阻塞了。
8榆浓、死鎖
????????事務(wù)不同線程之間存在循環(huán)資源依賴于未,你依賴我,我依賴你陡鹃,都在互相等待沉眶,就會(huì)死鎖,解決的辦法有:
????????●? 盡量在所有事務(wù)中杉适,對(duì)表的執(zhí)行順序保持一致谎倔;
????????●? innoDB有鎖的超時(shí)機(jī)制,默認(rèn)50S猿推,超時(shí)自動(dòng)回滾片习,這種方式不太方便,50S時(shí)間太長(zhǎng)了蹬叭,如果設(shè)置短一點(diǎn)藕咏,又會(huì)把很多不是死鎖的誤判成死鎖;
????????●? 一般情況下都需要開(kāi)啟死鎖檢測(cè)秽五,這樣每次加鎖的時(shí)候孽查,都會(huì)掃描會(huì)不會(huì)死鎖,如果會(huì)死鎖坦喘,回滾盲再。
????????通過(guò)show engine innodb status 會(huì)打印死鎖日志,看看為什么死鎖瓣铣。
9答朋、樂(lè)觀鎖
????????上面講的庫(kù)鎖、表鎖棠笑、MDL鎖梦碗、next-key lock都是悲觀鎖,都是通過(guò)執(zhí)行相應(yīng)的SQL語(yǔ)句,由數(shù)據(jù)庫(kù)來(lái)加鎖的洪规。
????????對(duì)于寫(xiě)入多的并發(fā)場(chǎng)景印屁,我們可以使用悲觀鎖,但是如果是讀多寫(xiě)少的并發(fā)場(chǎng)景斩例,我們還可以使用樂(lè)觀鎖库车,在代碼層面解決并發(fā)問(wèn)題。因?yàn)橛K瑢?xiě)入少的話柠衍,并發(fā)沖突的問(wèn)題就很少,因此可以不用考慮加悲觀鎖晶乔。
????????實(shí)現(xiàn)樂(lè)觀鎖的方式是加一個(gè)新的字段version(數(shù)據(jù)的版本號(hào))來(lái)實(shí)行樂(lè)觀鎖珍坊,具體步驟如下:
????????1)加一個(gè)新字段,version
????????2)先讀表里面的數(shù)據(jù)正罢,拿到我們需要更新的那行數(shù)據(jù)的version
????????3)更新數(shù)據(jù)時(shí)阵漏,比較當(dāng)前數(shù)據(jù)庫(kù)數(shù)據(jù)的version與第二步取到的version的值是否相等:
????????Update xxx set xxxValue = “ABABA” and version = versionValue+1 where version = versionValue
????????這樣就算兩個(gè)線程都來(lái)執(zhí)行這條語(yǔ)句,第一條執(zhí)行完以后翻具,第二條也無(wú)法執(zhí)行履怯,因此version變了。