国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

一文帶你了解MySQL之鎖

這篇具有很好參考價(jià)值的文章主要介紹了一文帶你了解MySQL之鎖。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

一、解決并發(fā)事務(wù)帶來問題的兩種基本方式

上一篇文章主要學(xué)習(xí)了事務(wù)并發(fā)執(zhí)行時(shí)可能帶來的各種問題,并發(fā)事務(wù)訪問相同記錄的情況我們大致可以劃分為3種:

  • 讀-讀情況:即并發(fā)事務(wù)相繼讀取相同的記錄,我們需要知道的是讀取操作本身不會(huì)對(duì)記錄有一毛錢影響,并不會(huì)引起什么問題,所以允許這種情況的發(fā)生。

  • 寫-寫情況:即并發(fā)事務(wù)相繼對(duì)相同的記錄做出改動(dòng),我們前邊說過,在這種情況下會(huì)發(fā)生臟寫的問題,任何一種隔離級(jí)別都不允許這種問題的發(fā)生。所以在多個(gè)未提交事務(wù)相繼對(duì)一條記錄做改動(dòng)時(shí),需要讓它們排隊(duì)執(zhí)行,這個(gè)排隊(duì)的過程其實(shí)是通過來實(shí)現(xiàn)的。這個(gè)所謂的其實(shí)是一個(gè)內(nèi)存中的結(jié)構(gòu),在事務(wù)執(zhí)行前本來是沒有鎖的,也就是說一開始是沒有鎖結(jié)構(gòu)和記錄進(jìn)行關(guān)聯(lián)的,如圖所示:

    一文帶你了解MySQL之鎖
    當(dāng)一個(gè)事務(wù)想對(duì)這條記錄做改動(dòng)時(shí),首先會(huì)看看內(nèi)存中有沒有與這條記錄關(guān)聯(lián)的鎖結(jié)構(gòu),當(dāng)沒有的時(shí)候就會(huì)在內(nèi)存中生成一個(gè)鎖結(jié)構(gòu)與之關(guān)聯(lián)。比方說事務(wù)T1要對(duì)這條記錄做改動(dòng),就需要生成一個(gè)鎖結(jié)構(gòu)與之關(guān)聯(lián):

    一文帶你了解MySQL之鎖

    其實(shí)在鎖結(jié)構(gòu)里有很多信息,我們現(xiàn)在只把兩個(gè)比較重要的屬性拿了出來:

    • trx信息:代表這個(gè)鎖結(jié)構(gòu)是哪個(gè)事務(wù)生成的

    • is_waiting:代表當(dāng)前事務(wù)是否在等待

    如圖所示,當(dāng)事務(wù)T1改動(dòng)了這條記錄后,就生成了一個(gè)鎖結(jié)構(gòu)與該記錄關(guān)聯(lián),因?yàn)橹皼]有別的事務(wù)為這條記錄加鎖,所以is_waiting屬性就是false,我們把這個(gè)場景就稱之為獲取鎖成功,或者加鎖成功,然后就可以繼續(xù)執(zhí)行操作了。

    事務(wù)T1提交之前,另一個(gè)事務(wù)T2也想對(duì)該記錄做改動(dòng),那么先去看看有沒有鎖結(jié)構(gòu)與這條記錄關(guān)聯(lián),發(fā)現(xiàn)有一個(gè)鎖結(jié)構(gòu)與之關(guān)聯(lián)后,然后也生成了一個(gè)鎖結(jié)構(gòu)與這條記錄關(guān)聯(lián),不過鎖結(jié)構(gòu)的is_waiting屬性值為true,表示當(dāng)前事務(wù)需要等待,我們把這個(gè)場景就稱之為獲取鎖失敗,或者加鎖失敗,或者沒有成功的獲取到鎖,畫個(gè)圖表示就是這樣:

    一文帶你了解MySQL之鎖

    事務(wù)T1提交之后,就會(huì)把該事務(wù)生成的鎖結(jié)構(gòu)釋放掉,然后看看還有沒有別的事務(wù)在等待獲取鎖,發(fā)現(xiàn)了事務(wù)T2還在等待獲取鎖,所以把事務(wù)T2對(duì)應(yīng)的鎖結(jié)構(gòu)的is_waiting屬性設(shè)置為false,然后把該事務(wù)對(duì)應(yīng)的線程喚醒,讓它繼續(xù)執(zhí)行,此時(shí)事務(wù)T2就算獲取到鎖了。效果圖就是這樣:

    一文帶你了解MySQL之鎖
    我們總結(jié)一下后續(xù)內(nèi)容中可能用到的幾種說法,以免大家后面混淆:

    • 不加鎖:意思就是不需要在內(nèi)存中生成對(duì)應(yīng)的鎖結(jié)構(gòu),可以直接執(zhí)行操作。

    • 獲取鎖成功,或者加鎖成功:意思就是在內(nèi)存中生成了對(duì)應(yīng)的鎖結(jié)構(gòu),而且鎖結(jié)構(gòu)的is_waiting屬性為false,也就是事務(wù)可以繼續(xù)執(zhí)行操作。

    • 獲取鎖失敗,或者加鎖失敗,或者沒有獲取到鎖:意思就是在內(nèi)存中生成了對(duì)應(yīng)的鎖結(jié)構(gòu),不過鎖結(jié)構(gòu)的is_waiting屬性為true,也就是事務(wù)需要等待,不可以繼續(xù)執(zhí)行操作。

  • 讀-寫寫-讀情況:也就是一個(gè)事務(wù)進(jìn)行讀取操作,另一個(gè)進(jìn)行改動(dòng)操作。我們前邊說過,這種情況下可能發(fā)生臟讀、不可重復(fù)讀、幻讀的問題

    小提示:
    幻讀問題的產(chǎn)生是因?yàn)槟硞€(gè)事務(wù)讀了一個(gè)范圍的記錄,之后別的事務(wù)在該范圍內(nèi)插入了新記錄,該事務(wù)再次讀取該范圍的記錄時(shí),可以讀到新插入的記錄,所以幻讀問題準(zhǔn)確的說并不是因?yàn)樽x取和寫入一條相同記錄而產(chǎn)生的,這一點(diǎn)要注意一下

    在上一篇文章中,我們也知道SQL標(biāo)準(zhǔn)規(guī)定不同隔離級(jí)別下可能發(fā)生的問題也不一樣:

    • READ UNCOMMITTED隔離級(jí)別下,臟讀、不可重復(fù)讀幻讀都可能發(fā)生

    • READ COMMITTED隔離級(jí)別下,不可重復(fù)讀、幻讀可能發(fā)生,臟讀不可以發(fā)生

    • REPEATABLE READ隔離級(jí)別下,幻讀可能發(fā)生,臟讀不可重復(fù)讀不可以發(fā)生

    • SERIALIZABLE隔離級(jí)別下,上述問題都不可以發(fā)生

    不過各個(gè)數(shù)據(jù)庫廠商對(duì)SQL標(biāo)準(zhǔn)的支持都可能不一樣,與SQL標(biāo)準(zhǔn)不同的一點(diǎn)就是,MySQL在REPEATABLE READ隔離級(jí)別實(shí)際上就已經(jīng)解決了幻讀問題

    怎么解決臟讀不可重復(fù)讀、幻讀這些問題呢?其實(shí)有兩種可選的解決方案:

    • 方案一:讀操作利用多版本并發(fā)控制(MVCC),寫操作進(jìn)行加鎖

      所謂的MVCC我們?cè)谇耙黄恼掠羞^詳細(xì)的描述,就是通過生成一個(gè)ReadView,然后通過ReadView找到符合條件的記錄版本(歷史版本是由undo日志構(gòu)建的),其實(shí)就像是在生成ReadView的那個(gè)時(shí)刻做了一次時(shí)間靜止(就像用相機(jī)拍了一個(gè)快照),查詢語句只能讀到在生成ReadView之前已提交事務(wù)所做的更改,在生成ReadView之前未提交的事務(wù)或者之后才開啟的事務(wù)所做的更改是看不到的。而寫操作肯定針對(duì)的是最新版本的記錄,讀記錄的歷史版本和改動(dòng)記錄的最新版本本身并不沖突,也就是采用MVCC時(shí),讀-寫操作并不沖突。

      小提示:
      我們說過普通的SELECT語句在READ COMMITTED和REPEATABLE READ隔離級(jí)別下會(huì)使用到MVCC讀取記錄。在READ COMMITTED隔離級(jí)別下,一個(gè)事務(wù)在執(zhí)行過程中每次執(zhí)行SELECT操作時(shí)都會(huì)生成一個(gè)ReadView,ReadView的存在本身就保證了事務(wù)不可以讀取到未提交的事務(wù)所做的更改,也就是避免了臟讀現(xiàn)象;REPEATABLE READ隔離級(jí)別下,一個(gè)事務(wù)在執(zhí)行過程中只有第一次執(zhí)行SELECT操作才會(huì)生成一個(gè)ReadView,之后的SELECT操作都復(fù)用這個(gè)ReadView,這樣也就避免了不可重復(fù)讀和幻讀的問題

    • 方案二:讀、寫操作都采用加鎖的方式

      如果我們的一些業(yè)務(wù)場景不允許讀取記錄的舊版本,而是每次都必須去讀取記錄的最新版本,比方在銀行存款的事務(wù)中,你需要先把賬戶的余額讀出來,然后將其加上本次存款的數(shù)額,最后再寫到數(shù)據(jù)庫中。在將賬戶余額讀取出來后,就不想讓別的事務(wù)再訪問該余額,直到本次存款事務(wù)執(zhí)行完成,其他事務(wù)才可以訪問賬戶的余額。這樣在讀取記錄的時(shí)候也就需要對(duì)其進(jìn)行加鎖操作,這樣也就意味著讀操作和寫操作也像寫-寫操作那樣排隊(duì)執(zhí)行

      小提示:
      我們說臟讀的產(chǎn)生是因?yàn)楫?dāng)前事務(wù)讀取了另一個(gè)未提交事務(wù)寫的一條記錄,如果另一個(gè)事務(wù)在寫記錄的時(shí)候就給這條記錄加鎖,那么當(dāng)前事務(wù)就無法繼續(xù)讀取該記錄了,所以也就不會(huì)有臟讀問題的產(chǎn)生了。不可重復(fù)讀的產(chǎn)生是因?yàn)楫?dāng)前事務(wù)先讀取一條記錄,另外一個(gè)事務(wù)對(duì)該記錄做了改動(dòng)之后并提交之后,當(dāng)前事務(wù)再次讀取時(shí)會(huì)獲得不同的值,如果在當(dāng)前事務(wù)讀取記錄時(shí)就給該記錄加鎖,那么另一個(gè)事務(wù)就無法修改該記錄,自然也不會(huì)發(fā)生不可重復(fù)讀了。我們說幻讀問題的產(chǎn)生是因?yàn)楫?dāng)前事務(wù)讀取了一個(gè)范圍的記錄,然后另外的事務(wù)向該范圍內(nèi)插入了新記錄,當(dāng)前事務(wù)再次讀取該范圍的記錄時(shí)發(fā)現(xiàn)了新插入的新記錄,我們把新插入的那些記錄稱之為幻影記錄。采用加鎖的方式解決幻讀問題就有那么一丟丟麻煩了,因?yàn)楫?dāng)前事務(wù)在第一次讀取記錄時(shí)那些幻影記錄并不存在,所以讀取的時(shí)候加鎖就有點(diǎn)尷尬 —— 因?yàn)槟悴⒉恢澜o誰加鎖,沒關(guān)系,這難不倒InnoDB,我們稍后揭曉答案,稍安勿躁。

很明顯,采用MVCC方式的話,讀-寫操作彼此并不沖突,性能更高,采用加鎖方式的話,讀-寫操作彼此需要排隊(duì)執(zhí)行,影響性能。一般情況下我們當(dāng)然愿意采用MVCC來解決讀-寫操作并發(fā)執(zhí)行的問題,但是業(yè)務(wù)在某些特殊情況下,要求必須采用加鎖的方式執(zhí)行,那也是沒有辦法的事。

1.1 一致性讀(Consistent Reads)

事務(wù)利用MVCC進(jìn)行的讀取操作稱之為一致性讀,或者一致性無鎖讀,有的地方也稱之為快照讀。所有普通的SELECT語句(plain SELECT)在READ COMMITTED、REPEATABLE READ隔離級(jí)別下都算是一致性讀,比如:

SELECT * FROM t;
SELECT * FROM t1 INNER JOIN t2 ON t1.col1 = t2.col2

我們需要知道的是,一致性讀并不會(huì)對(duì)表中的任何記錄做加鎖操作,其他事務(wù)可以自由的對(duì)表中的記錄做改動(dòng)。

1.2 鎖定讀(Locking Reads)

1.2.1 共享鎖和獨(dú)占鎖

我們前邊說過,并發(fā)事務(wù)的讀-讀情況并不會(huì)引起什么問題,不過對(duì)于寫-寫、讀-寫寫-讀這些情況可能會(huì)引起一些問題,需要使用MVCC或者加鎖的方式來解決它們。在使用加鎖的方式解決問題時(shí),由于既要允許讀-讀情況不受影響,又要使寫-寫、讀-寫寫-讀情況中的操作相互阻塞,所以MySQL給鎖分了個(gè)類:

  • 共享鎖,英文名:Shared Locks,簡稱S鎖。在事務(wù)要讀取一條記錄時(shí),需要先獲取該記錄的S鎖。

  • 獨(dú)占鎖,也常稱排他鎖,英文名:Exclusive Locks,簡稱X鎖。在事務(wù)要改動(dòng)一條記錄時(shí),需要先獲取該記錄的X鎖。

假如事務(wù)T1首先獲取了一條記錄的S鎖之后,事務(wù)T2接著也要訪問這條記錄:

  • 如果事務(wù)T2想要再獲取一個(gè)記錄的S鎖,那么事務(wù)T2也會(huì)獲得該鎖,也就意味著事務(wù)T1T2在該記錄上同時(shí)持有S鎖。

  • 如果事務(wù)T2想要再獲取一個(gè)記錄的X鎖,那么此操作會(huì)被阻塞,直到事務(wù)T1提交之后將S鎖釋放掉。

如果事務(wù)T1首先獲取了一條記錄的X鎖之后,那么不管事務(wù)T2接著想獲取該記錄的S鎖還是X鎖都會(huì)被阻塞,直到事務(wù)T1提交。

所以我們說S鎖S鎖是兼容的,S鎖X鎖是不兼容的,X鎖X鎖也是不兼容的,畫個(gè)表表示一下就是這樣:

兼容性 X S
X 不兼容 不兼容
S 不兼容 兼容

1.2.2 鎖定讀的語句

我們前邊說在采用加鎖方式解決臟讀、不可重復(fù)讀、幻讀這些問題時(shí),讀取一條記錄時(shí)需要獲取一下該記錄的S鎖,其實(shí)這是不嚴(yán)謹(jǐn)?shù)模袝r(shí)候想在讀取記錄時(shí)就獲取記錄的X鎖,來禁止別的事務(wù)讀寫該記錄,為此MySQL的提出了兩種比較特殊的SELECT語句格式:

對(duì)讀取的記錄加S鎖:

SELECT ... LOCK IN SHARE MODE;

也就是在普通的SELECT語句后邊加LOCK IN SHARE MODE,如果當(dāng)前事務(wù)執(zhí)行了該語句,那么它會(huì)為讀取到的記錄加S鎖,這樣允許別的事務(wù)繼續(xù)獲取這些記錄的S鎖(比方說別的事務(wù)也使用SELECT ... LOCK IN SHARE MODE語句來讀取這些記錄),但是不能獲取這些記錄的X鎖(比方說使用SELECT ... FOR UPDATE語句來讀取這些記錄,或者直接修改這些記錄)。如果別的事務(wù)想要獲取這些記錄的X鎖,那么它們會(huì)阻塞,直到當(dāng)前事務(wù)提交之后將這些記錄上的S鎖釋放掉

對(duì)讀取的記錄加X鎖:

SELECT ... FOR UPDATE;

也就是在普通的SELECT語句后邊加FOR UPDATE,如果當(dāng)前事務(wù)執(zhí)行了該語句,那么它會(huì)為讀取到的記錄加X鎖,這樣既不允許別的事務(wù)獲取這些記錄的S鎖(比方說別的事務(wù)使用SELECT ... LOCK IN SHARE MODE語句來讀取這些記錄),也不允許獲取這些記錄的X鎖(比如說使用SELECT ... FOR UPDATE語句來讀取這些記錄,或者直接修改這些記錄)。如果別的事務(wù)想要獲取這些記錄的S鎖或者X鎖,那么它們會(huì)阻塞,直到當(dāng)前事務(wù)提交之后將這些記錄上的X鎖釋放掉

關(guān)于更多鎖定讀加鎖細(xì)節(jié)我們稍后會(huì)詳細(xì)講解,稍安勿躁

1.3 寫操作

我們平常所用到的寫操作無非是DELETEUPDATE、INSERT這三種

  • DELETE:對(duì)一條記錄做DELETE操作的過程其實(shí)是先在B+樹中定位到這條記錄的位置,然后獲取一下這條記錄的X鎖,然后再執(zhí)行delete mark操作。我們也可以把這個(gè)定位待刪除記錄在B+樹中位置的過程看成是一個(gè)獲取X鎖鎖定讀。

  • UPDATE:在對(duì)一條記錄做UPDATE操作時(shí)分為三種情況:

    • 如果未修改該記錄的鍵值并且被更新的列占用的存儲(chǔ)空間在修改前后未發(fā)生變化,則先在B+樹中定位到這條記錄的位置,然后再獲取一下記錄的X鎖,最后在原記錄的位置進(jìn)行修改操作。其實(shí)我們也可以把這個(gè)定位待修改記錄在B+樹中位置的過程看成是一個(gè)獲取X鎖鎖定讀

    • 如果未修改該記錄的鍵值并且至少有一個(gè)被更新的列占用的存儲(chǔ)空間在修改前后發(fā)生變化,則先在B+樹中定位到這條記錄的位置,然后獲取一下記錄的X鎖,將該記錄徹底刪除掉(就是把記錄徹底移入垃圾鏈表),最后再插入一條新記錄。這個(gè)定位待修改記錄在B+樹中位置的過程看成是一個(gè)獲取X鎖鎖定讀,新插入的記錄由INSERT操作提供的隱式鎖進(jìn)行保護(hù)。

    • 如果修改了該記錄的鍵值,則相當(dāng)于在原記錄上做DELETE操作之后再來一次INSERT操作,加鎖操作就需要按照DELETEINSERT的規(guī)則進(jìn)行了。

  • INSERT:一般情況下,新插入一條記錄的操作并不加鎖,InnoDB通過一種稱之為隱式鎖來保護(hù)這條新插入的記錄在本事務(wù)提交前不被別的事務(wù)訪問,更多細(xì)節(jié)我們后邊看哈~

    小提示:
    當(dāng)然,在一些特殊情況下INSERT操作也是會(huì)獲取鎖的,具體情況我們后邊學(xué)習(xí)

二、多粒度鎖

我們前邊提到的都是針對(duì)記錄的,也可以被稱之為行級(jí)鎖或者行鎖,對(duì)一條記錄加鎖影響的也只是這條記錄而已,我們就說這個(gè)鎖的粒度比較細(xì);其實(shí)一個(gè)事務(wù)也可以在表級(jí)別進(jìn)行加鎖,自然就被稱之為表級(jí)鎖或者表鎖,對(duì)一個(gè)表加鎖影響整個(gè)表中的記錄,我們就說這個(gè)鎖的粒度比較粗。給表加的鎖也可以分為共享鎖S鎖)和獨(dú)占鎖X鎖

  • 給表加S鎖:如果一個(gè)事務(wù)給表加了S鎖,那么:

    • 別的事務(wù)可以繼續(xù)獲得該表的S鎖

    • 別的事務(wù)可以繼續(xù)獲得該表中的某些記錄的S鎖

    • 別的事務(wù)不可以繼續(xù)獲得該表的X鎖

    • 別的事務(wù)不可以繼續(xù)獲得該表中的某些記錄的X鎖

  • 給表加X鎖:如果一個(gè)事務(wù)給表加了X鎖(意味著該事務(wù)要獨(dú)占這個(gè)表),那么:

    • 別的事務(wù)不可以繼續(xù)獲得該表的S鎖

    • 別的事務(wù)不可以繼續(xù)獲得該表中的某些記錄的S鎖

    • 別的事務(wù)不可以繼續(xù)獲得該表的X鎖

    • 別的事務(wù)不可以繼續(xù)獲得該表中的某些記錄的X鎖

上邊看著有點(diǎn)啰嗦,為了更好的理解這個(gè)表級(jí)別的S鎖X鎖,我們舉一個(gè)現(xiàn)實(shí)生活中的例子。不知道各位同學(xué)都上過大學(xué)沒,我們以大學(xué)教學(xué)樓中的教室為例來分析一下加鎖的情況:

  • 教室一般都是公用的,我們可以隨便選教室進(jìn)去上自習(xí)。當(dāng)然,教室不是自家的,一間教室可以容納很多同學(xué)同時(shí)上自習(xí),每當(dāng)一個(gè)人進(jìn)去上自習(xí),就相當(dāng)于在教室門口掛了一把S鎖,如果很多同學(xué)都進(jìn)去上自習(xí),相當(dāng)于教室門口掛了很多把S鎖(類似行級(jí)別的S鎖)。

  • 有的時(shí)候教室會(huì)進(jìn)行檢修,比方說換地板,換天花板,換燈管啥的,這些維修項(xiàng)目并不能同時(shí)開展。如果教室針對(duì)某個(gè)項(xiàng)目進(jìn)行檢修,就不允許別的同學(xué)來上自習(xí),也不允許其他維修項(xiàng)目進(jìn)行,此時(shí)相當(dāng)于教室門口會(huì)掛一把X鎖(類似行級(jí)別的X鎖)。

上邊提到的這兩種鎖都是針對(duì)教室而言的,不過有時(shí)候我們會(huì)有一些特殊的需求:

  • 有領(lǐng)導(dǎo)要來參觀教學(xué)樓的環(huán)境。

    校領(lǐng)導(dǎo)考慮并不想影響同學(xué)們上自習(xí),但是此時(shí)不能有教室處于維修狀態(tài),所以可以在教學(xué)樓門口放置一把S鎖(類似表級(jí)別的S鎖)。此時(shí):

    • 來上自習(xí)的學(xué)生們看到教學(xué)樓門口有S鎖,可以繼續(xù)進(jìn)入教學(xué)樓上自習(xí)。

    • 修理工看到教學(xué)樓門口有S鎖,則先在教學(xué)樓門口等著,啥時(shí)候領(lǐng)導(dǎo)走了,把教學(xué)樓的S鎖撤掉再進(jìn)入教學(xué)樓維修。

  • 學(xué)校要占用教學(xué)樓進(jìn)行考試。

    此時(shí)不允許教學(xué)樓中有正在上自習(xí)的教室,也不允許對(duì)教室進(jìn)行維修。所以可以在教學(xué)樓門口放置一把X鎖(類似表級(jí)別的X鎖)。此時(shí):

    • 來上自習(xí)的學(xué)生們看到教學(xué)樓門口有X鎖,則需要在教學(xué)樓門口等著,啥時(shí)候考試結(jié)束,把教學(xué)樓的X鎖撤掉再進(jìn)入教學(xué)樓上自習(xí)。

    • 修理工看到教學(xué)樓門口有X鎖,則先在教學(xué)樓門口等著,啥時(shí)候考試結(jié)束,把教學(xué)樓的X鎖撤掉再進(jìn)入教學(xué)樓維修。

但是這里頭有兩個(gè)問題:

  • 如果我們想對(duì)教學(xué)樓整體上S鎖,首先需要確保教學(xué)樓中的沒有正在維修的教室,如果有正在維修的教室,需要等到維修結(jié)束才可以對(duì)教學(xué)樓整體上S鎖。

  • 如果我們想對(duì)教學(xué)樓整體上X鎖,首先需要確保教學(xué)樓中的沒有上自習(xí)的教室以及正在維修的教室,如果有上自習(xí)的教室或者正在維修的教室,需要等到全部上自習(xí)的同學(xué)都上完自習(xí)離開,以及維修工維修完教室離開后才可以對(duì)教學(xué)樓整體上X鎖

我們?cè)趯?duì)教學(xué)樓整體上鎖(表鎖)時(shí),怎么知道教學(xué)樓中有沒有教室已經(jīng)被上鎖(行鎖)了呢?依次檢查每一間教室門口有沒有上鎖?那這效率也太慢了吧!遍歷是不可能遍歷的,這輩子也不可能遍歷的,于是InnoDB的提出了一種稱之為意向鎖(英文名:Intention Locks)的東東:

  • 意向共享鎖,英文名:Intention Shared Lock,簡稱IS鎖。當(dāng)事務(wù)準(zhǔn)備在某條記錄上加S鎖時(shí),需要先在表級(jí)別加一個(gè)IS鎖。

  • 意向獨(dú)占鎖,英文名:Intention Exclusive Lock,簡稱IX鎖。當(dāng)事務(wù)準(zhǔn)備在某條記錄上加X鎖時(shí),需要先在表級(jí)別加一個(gè)IX鎖。

視角回到教學(xué)樓和教室上來:

  • 如果有學(xué)生到教室中上自習(xí),那么他先在整棟教學(xué)樓門口放一把IS鎖表級(jí)鎖),然后再到教室門口放一把S鎖(行鎖)。

  • 如果有維修工到教室中維修,那么它先在整棟教學(xué)樓門口放一把IX鎖表級(jí)鎖),然后再到教室門口放一把X鎖行鎖)。

之后:

  • 如果有領(lǐng)導(dǎo)要參觀教學(xué)樓,也就是想在教學(xué)樓門口前放S鎖表鎖)時(shí),首先要看一下教學(xué)樓門口有沒有IX鎖,如果有,意味著有教室在維修,需要等到維修結(jié)束把IX鎖撤掉后才可以在整棟教學(xué)樓上加S鎖。

  • 如果有考試要占用教學(xué)樓,也就是想在教學(xué)樓門口前放X鎖表鎖)時(shí),首先要看一下教學(xué)樓門口有沒有IS鎖IX鎖,如果有,意味著有教室在上自習(xí)或者維修,需要等到學(xué)生們上完自習(xí)以及維修結(jié)束把IS鎖IX鎖撤掉后才可以在整棟教學(xué)樓上加X鎖。

小提示:
學(xué)生在教學(xué)樓門口加IS鎖時(shí),是不關(guān)心教學(xué)樓門口是否有IX鎖的,維修工在教學(xué)樓門口加IX鎖時(shí),是不關(guān)心教學(xué)樓門口是否有IS鎖或者其他IX鎖的。IS和IX鎖只是為了判斷當(dāng)前時(shí)間教學(xué)樓里有沒有被占用的教室用的,也就是在對(duì)教學(xué)樓加S鎖或者X鎖時(shí)才會(huì)用到。

總結(jié)一下:IS、IX鎖表級(jí)鎖,它們的提出僅僅為了在之后加表級(jí)別的S鎖X鎖時(shí)可以快速判斷表中的記錄是否被上鎖,以避免用遍歷的方式來查看表中有沒有上鎖的記錄,也就是說其實(shí)IS鎖IX鎖是兼容的,IX鎖IX鎖是兼容的。我們畫個(gè)表來看一下表級(jí)別的各種鎖的兼容性:

兼容性 X IX S IS
X 不兼容 不兼容 不兼容 不兼容
IX 不兼容 兼容 不兼容 兼容
S 不兼容 不兼容 兼容 兼容
IS 不兼容 兼容 兼容 兼容

三、MySQL中的行鎖和表鎖

上邊說的都算是些理論知識(shí),其實(shí)MySQL支持多種存儲(chǔ)引擎,不同存儲(chǔ)引擎對(duì)鎖的支持也是不一樣的。當(dāng)然,我們重點(diǎn)還是討論InnoDB存儲(chǔ)引擎中的鎖,其他的存儲(chǔ)引擎只是稍微提一下~

3.1 其他存儲(chǔ)引擎中的鎖

對(duì)于MyISAM、MEMORYMERGE這些存儲(chǔ)引擎來說,它們只支持表級(jí)鎖,而且這些引擎并不支持事務(wù),所以使用這些存儲(chǔ)引擎的鎖一般都是針對(duì)當(dāng)前會(huì)話來說的。比方說在Session 1中對(duì)一個(gè)表執(zhí)行SELECT操作,就相當(dāng)于為這個(gè)表加了一個(gè)表級(jí)別的S鎖,如果在SELECT操作未完成時(shí),Session 2中對(duì)這個(gè)表執(zhí)行UPDATE操作,相當(dāng)于要獲取表的X鎖,此操作會(huì)被阻塞,直到Session 1中的SELECT操作完成,釋放掉表級(jí)別的S鎖后,Session 2中對(duì)這個(gè)表執(zhí)行UPDATE操作才能繼續(xù)獲取X鎖,然后執(zhí)行具體的更新語句

3.2 InnoDB存儲(chǔ)引擎中的鎖

InnoDB存儲(chǔ)引擎既支持表鎖,也支持行鎖表鎖實(shí)現(xiàn)簡單,占用資源較少,不過粒度很粗,有時(shí)候你僅僅需要鎖住幾條記錄,但使用表鎖的話相當(dāng)于為表中的所有記錄都加鎖,所以性能比較差。行鎖粒度更細(xì),可以實(shí)現(xiàn)更精準(zhǔn)的并發(fā)控制。下邊我們?cè)敿?xì)看一下

3.2.1 InnoDB中的表級(jí)鎖

表級(jí)別的S鎖、X鎖

在對(duì)某個(gè)表執(zhí)行SELECT、INSERTDELETE、UPDATE語句時(shí),InnoDB存儲(chǔ)引擎是不會(huì)為這個(gè)表添加表級(jí)別S鎖或者X鎖的。

另外,在對(duì)某個(gè)表執(zhí)行一些諸如ALTER TABLE、DROP TABLE這類的DDL語句時(shí),其他事務(wù)對(duì)這個(gè)表并發(fā)執(zhí)行諸如SELECT、INSERTDELETE、UPDATE的語句會(huì)發(fā)生阻塞,同理,某個(gè)事務(wù)中對(duì)某個(gè)表執(zhí)行SELECTINSERT、DELETE、UPDATE語句時(shí),在其他會(huì)話中對(duì)這個(gè)表執(zhí)行DDL語句也會(huì)發(fā)生阻塞。這個(gè)過程其實(shí)是通過在server層使用一種稱之為元數(shù)據(jù)鎖(英文名:Metadata Locks,簡稱MDL)來實(shí)現(xiàn)的,一般情況下也不會(huì)使用InnoDB存儲(chǔ)引擎自己提供的表級(jí)別的S鎖X鎖。

小提示:
在事務(wù)簡介的文章中我們說過,DDL語句執(zhí)行時(shí)會(huì)隱式的提交當(dāng)前會(huì)話中的事務(wù),這主要是DDL語句的執(zhí)行一般都會(huì)在若干個(gè)特殊事務(wù)中完成,在開啟這些特殊事務(wù)前,需要將當(dāng)前會(huì)話中的事務(wù)提交掉。

其實(shí)這個(gè)InnoDB存儲(chǔ)引擎提供的表級(jí)S鎖或者X鎖是相當(dāng)雞肋,只會(huì)在一些特殊情況下,比方說崩潰恢復(fù)過程中用到。不過我們還是可以手動(dòng)獲取一下的,比方說在系統(tǒng)變量autocommit=0,innodb_table_locks = 1時(shí),手動(dòng)獲取InnoDB存儲(chǔ)引擎提供的表t的S鎖或者X鎖可以這么寫:

  • LOCK TABLES t READ:InnoDB存儲(chǔ)引擎會(huì)對(duì)表t加表級(jí)別的S鎖。

  • LOCK TABLES t WRITE:InnoDB存儲(chǔ)引擎會(huì)對(duì)表t加表級(jí)別的X鎖

不過我們盡量避免在使用InnoDB存儲(chǔ)引擎的表上使用LOCK TABLES這樣的手動(dòng)鎖表語句,它們并不會(huì)提供什么額外的保護(hù),只是會(huì)降低并發(fā)能力而已。InnoDB的厲害之處還是實(shí)現(xiàn)了更細(xì)粒度的行鎖,關(guān)于表級(jí)別的S鎖X鎖大家了解一下就罷了。

表級(jí)別的IS鎖、IX鎖

當(dāng)我們?cè)趯?duì)使用InnoDB存儲(chǔ)引擎的表的某些記錄加S鎖之前,那就需要先在表級(jí)別加一個(gè)IS鎖,當(dāng)我們?cè)趯?duì)使用InnoDB存儲(chǔ)引擎的表的某些記錄加X鎖之前,那就需要先在表級(jí)別加一個(gè)IX鎖。IS鎖IX鎖的使命只是為了后續(xù)在加表級(jí)別的S鎖X鎖時(shí)判斷表中是否有已經(jīng)被加鎖的記錄,以避免用遍歷的方式來查看表中有沒有上鎖的記錄。更多關(guān)于IS鎖IX鎖的解釋我們上邊已經(jīng)講解了,就不贅述了。

表級(jí)別的AUTO-INC鎖

在使用MySQL過程中,我們可以為表的某個(gè)列添加AUTO_INCREMENT屬性,之后在插入記錄時(shí),可以不指定該列的值,系統(tǒng)會(huì)自動(dòng)為它賦上遞增的值,比方說我們有一個(gè)表:

CREATE TABLE t (
    id INT NOT NULL AUTO_INCREMENT,
    c VARCHAR(100),
    PRIMARY KEY (id)
);

由于這個(gè)表的id字段聲明了AUTO_INCREMENT,也就意味著在書寫插入語句時(shí)不需要為其賦值,比方說這樣:

INSERT INTO t(c) VALUES('aa'), ('bb');

上邊的插入語句并沒有為id列顯式賦值,所以系統(tǒng)會(huì)自動(dòng)為它賦上遞增的值,效果就是這樣:

mysql> SELECT * FROM t;
+----+------+
| id | c    |
+----+------+
|  1 | aa   |
|  2 | bb   |
+----+------+
2 rows in set (0.00 sec)

系統(tǒng)實(shí)現(xiàn)這種自動(dòng)給AUTO_INCREMENT修飾的列遞增賦值的原理主要是兩個(gè):

  • 采用AUTO-INC鎖,也就是在執(zhí)行插入語句時(shí)就在表級(jí)別加一個(gè)AUTO-INC鎖,然后為每條待插入記錄的AUTO_INCREMENT修飾的列分配遞增的值,在該語句執(zhí)行結(jié)束后,再把AUTO-INC鎖釋放掉。這樣一個(gè)事務(wù)在持有AUTO-INC鎖的過程中,其他事務(wù)的插入語句都要被阻塞,可以保證一個(gè)語句中分配的遞增值是連續(xù)的。

    如果我們的插入語句在執(zhí)行前不可以確定具體要插入多少條記錄(無法預(yù)計(jì)即將插入記錄的數(shù)量),比方說使用INSERT ... SELECT、REPLACE ... SELECT或者LOAD DATA這種插入語句,一般是使用AUTO-INC鎖為AUTO_INCREMENT修飾的列生成對(duì)應(yīng)的值。

    小提示:
    需要注意一下的是,這個(gè)AUTO-INC鎖的作用范圍只是單個(gè)插入語句,插入語句執(zhí)行完成后,這個(gè)鎖就被釋放了,跟我們之前介紹的鎖在事務(wù)結(jié)束時(shí)釋放是不一樣的

  • 采用一個(gè)輕量級(jí)的鎖,在為插入語句生成AUTO_INCREMENT修飾的列的值時(shí)獲取一下這個(gè)輕量級(jí)鎖,然后生成本次插入語句需要用到的AUTO_INCREMENT列的值之后,就把該輕量級(jí)鎖釋放掉,并不需要等到整個(gè)插入語句執(zhí)行完才釋放鎖。

    如果我們的插入語句在執(zhí)行前就可以確定具體要插入多少條記錄,比方說我們上邊舉的關(guān)于表t的例子中,在語句執(zhí)行前就可以確定要插入2條記錄,那么一般采用輕量級(jí)鎖的方式對(duì)AUTO_INCREMENT修飾的列進(jìn)行賦值。這種方式可以避免鎖定表,可以提升插入性能。

    小提示:
    InnoDB提供了一個(gè)稱之為innodb_autoinc_lock_mode的系統(tǒng)變量來控制到底使用上述兩種方式中的哪種來為AUTO_INCREMENT修飾的列進(jìn)行賦值
    當(dāng)innodb_autoinc_lock_mode值為0時(shí),一律采用AUTO-INC鎖;
    當(dāng)innodb_autoinc_lock_mode值為2時(shí),一律采用輕量級(jí)鎖;
    當(dāng)innodb_autoinc_lock_mode值為1時(shí),兩種方式混著來(也就是在插入記錄數(shù)量確定時(shí)采用輕量級(jí)鎖,不確定時(shí)使用AUTO-INC鎖)。
    不過當(dāng)innodb_autoinc_lock_mode值為2時(shí),可能會(huì)造成不同事務(wù)中的插入語句為AUTO_INCREMENT修飾的列生成的值是交叉的,在有主從復(fù)制的場景中是不安全的。

3.2.2 InnoDB中的行級(jí)鎖

很遺憾的通知大家一個(gè)不好的消息,上邊講的都是鋪墊,本章真正的重點(diǎn)才剛剛開始

行鎖,也稱為記錄鎖,顧名思義就是在記錄上加的鎖。不過InnoDB把一個(gè)行鎖玩出了各種花樣,也就是把行鎖分成了各種類型。換句話說即使對(duì)同一條記錄加行鎖,如果類型不同,起到的功效也是不同的。為了學(xué)習(xí)的順利發(fā)展,我們還是先將之前學(xué)習(xí)的MVCC時(shí)用到的表抄一遍:

mysql> CREATE TABLE hero(
	number INT PRIMARY KEY,
	name VARCHAR(4),
	country VARCHAR(2)
);
Query OK, 0 rows affected (0.03 sec)

我們主要是想用這個(gè)表存儲(chǔ)王者的英雄,然后向這個(gè)表里插入幾條記錄:

mysql> INSERT INTO hero VALUES
    (1, 'l劉備', '蜀國'),
    (3, 'z諸葛亮', '蜀國'),
    (8, 'c曹操', '蜀國'),
    (15, 'x項(xiàng)羽', '西楚'),
    (20, 's孫權(quán)', '吳國');
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0

小提示:
為啥要在’劉備’、‘曹操’、‘孫權(quán)’前邊加上’l’、‘c’、‘s’這幾個(gè)字母呀?這個(gè)主要是因?yàn)槲覀儾捎胾tf8mb4字符集,該字符集并沒有對(duì)應(yīng)的按照漢語拼音進(jìn)行排序的比較規(guī)則,也就是說’劉備’、‘曹操’、'孫權(quán)’這幾個(gè)字符串的排序并不是按照它們漢語拼音進(jìn)行排序的,所以在漢字前邊加上了漢字對(duì)應(yīng)的拼音的第一個(gè)字母,這樣在排序時(shí)就是按照漢語拼音進(jìn)行排序。

另外,我們故意把各條記錄number列的值搞得很分散,后邊會(huì)用到,稍安勿躁哈~我們把hero表中的聚簇索引的示意圖畫一下:

一文帶你了解MySQL之鎖
當(dāng)然,我們把B+樹的索引結(jié)構(gòu)做了一個(gè)超級(jí)簡化,只把索引中的記錄給拿了出來,我們這里只是想強(qiáng)調(diào)聚簇索引中的記錄是按照主鍵大小排序的,并且省略掉了聚簇索引中的隱藏列,大家心里明白就好(不理解索引結(jié)構(gòu)的同學(xué)可以去前邊的文章中查看)。

現(xiàn)在準(zhǔn)備工作做完了,下邊我們來看看都有哪些常用的行鎖類型。

  • Record Locks:我們前邊提到的記錄鎖就是這種類型,也就是僅僅把一條記錄鎖上,我決定給這種類型的鎖起一個(gè)比較不正經(jīng)的名字:正經(jīng)記錄鎖(請(qǐng)?jiān)试S我皮一下,我實(shí)在不知道該叫個(gè)啥名好)。官方的類型名稱為:LOCK_REC_NOT_GAP。比方說我們把number值為8的那條記錄加一個(gè)正經(jīng)記錄鎖的示意圖如下:
    一文帶你了解MySQL之鎖
    正經(jīng)記錄鎖是有S鎖X鎖之分的,讓我們分別稱之為S型正經(jīng)記錄鎖X型正經(jīng)記錄鎖吧,當(dāng)一個(gè)事務(wù)獲取了一條記錄的S型正經(jīng)記錄鎖后,其他事務(wù)也可以繼續(xù)獲取該記錄的S型正經(jīng)記錄鎖,但不可以繼續(xù)獲取X型正經(jīng)記錄鎖;當(dāng)一個(gè)事務(wù)獲取了一條記錄的X型正經(jīng)記錄鎖后,其他事務(wù)既不可以繼續(xù)獲取該記錄的S型正經(jīng)記錄鎖,也不可以繼續(xù)獲取X型正經(jīng)記錄鎖;

  • Gap Locks:我們說MySQLREPEATABLE READ隔離級(jí)別下是可以解決幻讀問題的,解決方案有兩種,可以使用MVCC方案解決,也可以采用加鎖方案解決。但是在使用加鎖方案解決時(shí)有個(gè)大問題,就是事務(wù)在第一次執(zhí)行讀取操作時(shí),那些幻影記錄尚不存在,我們無法給這些幻影記錄加上正經(jīng)記錄鎖。不過這難不倒InnoDB,他們提出了一種稱之為Gap Locks的鎖,官方的類型名稱為:LOCK_GAP,我們也可以簡稱為gap鎖。比方說我們把number值為8的那條記錄加一個(gè)gap鎖的示意圖如下:

    一文帶你了解MySQL之鎖
    如圖中為number值為8的記錄加了gap鎖,意味著不允許別的事務(wù)在number值為8的記錄前邊的間隙插入新記錄,其實(shí)就是number列的值(3, 8)這個(gè)區(qū)間的新記錄是不允許立即插入的。比方說有另外一個(gè)事務(wù)再想插入一條number值為4的新記錄,它定位到該條新記錄的下一條記錄的number值為8,而這條記錄上又有一個(gè)gap鎖,所以就會(huì)阻塞插入操作,直到擁有這個(gè)gap鎖的事務(wù)提交了之后,number列的值在區(qū)間(3, 8)中的新記錄才可以被插入。

    這個(gè)gap鎖的提出僅僅是為了防止插入幻影記錄而提出的,雖然有共享gap鎖獨(dú)占gap鎖這樣的說法,但是它們起到的作用都是相同的。而且如果你對(duì)一條記錄加了gap鎖(不論是共享gap鎖還是獨(dú)占gap鎖),并不會(huì)限制其他事務(wù)對(duì)這條記錄加正經(jīng)記錄鎖或者繼續(xù)加gap鎖

    不知道大家發(fā)現(xiàn)了一個(gè)問題沒,給一條記錄加了gap鎖只是不允許其他事務(wù)往這條記錄前邊的間隙插入新記錄,那對(duì)于最后一條記錄之后的間隙,也就是hero表中number值為20的記錄之后的間隙該咋辦呢?也就是說給哪條記錄加gap鎖才能阻止其他事務(wù)插入number值在(20, +∞)這個(gè)區(qū)間的新記錄呢?這時(shí)候應(yīng)該想起我們?cè)谇斑厙Z叨數(shù)據(jù)頁時(shí)介紹的兩條偽記錄了

    • Infimum記錄,表示該頁面中最小的記錄。

    • Supremum記錄,表示該頁面中最大的記錄。

    為了實(shí)現(xiàn)阻止其他事務(wù)插入number值在(20, +∞)這個(gè)區(qū)間的新記錄,我們可以給索引中的最后一條記錄,也就是number值為20的那條記錄所在頁面的Supremum記錄加上一個(gè)gap鎖,畫個(gè)圖就是這樣:

    一文帶你了解MySQL之鎖
    這樣就可以阻止其他事務(wù)插入number值在(20, +∞)這個(gè)區(qū)間的新記錄。為了大家理解方便,之后的索引示意圖中都會(huì)把這個(gè)Supremum記錄畫出來

  • Next-Key Locks:有時(shí)候我們既想鎖住某條記錄,又想阻止其他事務(wù)在該記錄前邊的間隙插入新記錄,所以InnoDB就提出了一種稱之為Next-Key Locks的鎖,官方的類型名稱為:LOCK_ORDINARY,我們也可以簡稱為next-key鎖。比方說我們把number值為8的那條記錄加一個(gè)next-key鎖的示意圖如下:

    一文帶你了解MySQL之鎖
    next-key鎖的本質(zhì)就是一個(gè)正經(jīng)記錄鎖和一個(gè)gap鎖的合體,它既能保護(hù)該條記錄,又能阻止別的事務(wù)將新記錄插入被保護(hù)記錄前邊的間隙。

  • Insert Intention Locks:我們說一個(gè)事務(wù)在插入一條記錄時(shí)需要判斷一下插入位置是不是被別的事務(wù)加了所謂的gap鎖next-key鎖也包含gap鎖,后邊就不強(qiáng)調(diào)了),如果有的話,插入操作需要等待,直到擁有gap鎖的那個(gè)事務(wù)提交。但是InnoDB規(guī)定事務(wù)在等待的時(shí)候也需要在內(nèi)存中生成一個(gè)鎖結(jié)構(gòu),表明有事務(wù)想在某個(gè)間隙中插入新記錄,但是現(xiàn)在在等待。InnoDB把這種類型的鎖命名為Insert Intention Locks,官方的類型名稱為:LOCK_INSERT_INTENTION,我們也可以稱為插入意向鎖。

    比方說我們把number值為8的那條記錄加一個(gè)插入意向鎖的示意圖如下:

    一文帶你了解MySQL之鎖
    為了讓大家徹底理解這個(gè)插入意向鎖的功能,我們還是舉個(gè)例子然后畫個(gè)圖表示一下。比方說現(xiàn)在T1number值為8的記錄加了一個(gè)gap鎖,然后T2T3分別想向hero表中插入number值分別為4、5的兩條記錄,所以現(xiàn)在為number值為8的記錄加的鎖的示意圖就如下所示:

    一文帶你了解MySQL之鎖

    小提示:
    我們?cè)阪i結(jié)構(gòu)中又新添了一個(gè)type屬性,表明該鎖的類型。稍后會(huì)全面介紹InnoDB存儲(chǔ)引擎中的一個(gè)鎖結(jié)構(gòu)到底長什么樣

    從圖中可以看到,由于T1持有gap鎖,所以T2T3需要生成一個(gè)插入意向鎖鎖結(jié)構(gòu)并且處于等待狀態(tài)。當(dāng)T1提交后會(huì)把它獲取到的鎖都釋放掉,這樣T2T3就能獲取到對(duì)應(yīng)的插入意向鎖了(本質(zhì)上就是把插入意向鎖對(duì)應(yīng)鎖結(jié)構(gòu)的is_waiting屬性改為false),T2T3之間也并不會(huì)相互阻塞,它們可以同時(shí)獲取到number值為8插入意向鎖,然后執(zhí)行插入操作。事實(shí)上插入意向鎖并不會(huì)阻止別的事務(wù)繼續(xù)獲取該記錄上任何類型的鎖插入意向鎖就是這么雞肋)。

  • 隱式鎖:我們前邊說一個(gè)事務(wù)在執(zhí)行INSERT操作時(shí),如果即將插入的間隙已經(jīng)被其他事務(wù)加了gap鎖,那么本次INSERT操作會(huì)阻塞,并且當(dāng)前事務(wù)會(huì)在該間隙上加一個(gè)插入意向鎖,否則一般情況下INSERT操作是不加鎖的。那如果一個(gè)事務(wù)首先插入了一條記錄(此時(shí)并沒有與該記錄關(guān)聯(lián)的鎖結(jié)構(gòu)),然后另一個(gè)事務(wù):

    • 立即使用SELECT ... LOCK IN SHARE MODE語句讀取這條記錄,也就是在要獲取這條記錄的S鎖,或者使用SELECT ... FOR UPDATE語句讀取這條記錄,也就是要獲取這條記錄的X鎖,該咋辦?

      如果允許這種情況的發(fā)生,那么可能產(chǎn)生臟讀問題。

    • 立即修改這條記錄,也就是要獲取這條記錄的X鎖,該咋辦?

      如果允許這種情況的發(fā)生,那么可能產(chǎn)生臟寫問題。

    這時(shí)候我們前邊嘮叨了很多遍的事務(wù)id又要起作用了。我們把聚簇索引二級(jí)索引中的記錄分開看一下:

    • 情景一:對(duì)于聚簇索引記錄來說,有一個(gè)trx_id隱藏列,該隱藏列記錄著最后改動(dòng)該記錄的事務(wù)id。那么如果在當(dāng)前事務(wù)中新插入一條聚簇索引記錄后,該記錄的trx_id隱藏列代表的的就是當(dāng)前事務(wù)的事務(wù)id,如果其他事務(wù)此時(shí)想對(duì)該記錄添加S鎖或者X鎖時(shí),首先會(huì)看一下該記錄的trx_id隱藏列代表的事務(wù)是否是當(dāng)前的活躍事務(wù),如果是的話,那么就幫助當(dāng)前事務(wù)創(chuàng)建一個(gè)X鎖(也就是為當(dāng)前事務(wù)創(chuàng)建一個(gè)鎖結(jié)構(gòu),is_waiting屬性是false),然后自己進(jìn)入等待狀態(tài)(也就是為自己也創(chuàng)建一個(gè)鎖結(jié)構(gòu),is_waiting屬性是true)。

    • 情景二:對(duì)于二級(jí)索引記錄來說,本身并沒有trx_id隱藏列,但是在二級(jí)索引頁面的Page Header部分有一個(gè)PAGE_MAX_TRX_ID屬性,該屬性代表對(duì)該頁面做改動(dòng)的最大的事務(wù)id,如果PAGE_MAX_TRX_ID屬性值小于當(dāng)前最小的活躍事務(wù)id,那么說明對(duì)該頁面做修改的事務(wù)都已經(jīng)提交了,否則就需要在頁面中定位到對(duì)應(yīng)的二級(jí)索引記錄,然后回表找到它對(duì)應(yīng)的聚簇索引記錄,然后再重復(fù)情景一的做法。

    通過上邊的敘述我們知道,一個(gè)事務(wù)對(duì)新插入的記錄可以不顯式的加鎖(生成一個(gè)鎖結(jié)構(gòu)),但是由于事務(wù)id的存在,相當(dāng)于加了一個(gè)隱式鎖。別的事務(wù)在對(duì)這條記錄加S鎖或者X鎖時(shí),由于隱式鎖的存在,會(huì)先幫助當(dāng)前事務(wù)生成一個(gè)鎖結(jié)構(gòu),然后自己再生成一個(gè)鎖結(jié)構(gòu)后進(jìn)入等待狀態(tài)

    小提示:
    除了插入意向鎖,在一些特殊情況下INSERT還會(huì)獲取一些鎖,我們稍后學(xué)習(xí)~

四、InnoDB鎖的內(nèi)存結(jié)構(gòu)

我們前邊說對(duì)一條記錄加鎖的本質(zhì)就是在內(nèi)存中創(chuàng)建一個(gè)鎖結(jié)構(gòu)與之關(guān)聯(lián),那么是不是一個(gè)事務(wù)對(duì)多條記錄加鎖,就要?jiǎng)?chuàng)建多個(gè)鎖結(jié)構(gòu)呢?比方說事務(wù)T1要執(zhí)行下邊這個(gè)語句:

# 事務(wù)T1
SELECT * FROM hero LOCK IN SHARE MODE;

很顯然這條語句需要為hero表中的所有記錄進(jìn)行加鎖,那是不是需要為每條記錄都生成一個(gè)鎖結(jié)構(gòu)呢?其實(shí)理論上創(chuàng)建多個(gè)鎖結(jié)構(gòu)沒問題,反而更容易理解,但是誰知道你在一個(gè)事務(wù)里想對(duì)多少記錄加鎖呢,如果一個(gè)事務(wù)要獲取10000條記錄的鎖,要生成10000個(gè)這樣的結(jié)構(gòu)也太虧了吧!InnoDB的決定在對(duì)不同記錄加鎖時(shí),如果符合下邊這些條件:

  • 在同一個(gè)事務(wù)中進(jìn)行加鎖操作

  • 被加鎖的記錄在同一個(gè)頁面中

  • 加鎖的類型是一樣的

  • 等待狀態(tài)是一樣的

那么這些記錄的鎖就可以被放到一個(gè)鎖結(jié)構(gòu)中。當(dāng)然,這么空口白牙的說有點(diǎn)兒抽象,我們還是畫個(gè)圖來看看InnoDB存儲(chǔ)引擎中的鎖結(jié)構(gòu)具體長啥樣吧:

一文帶你了解MySQL之鎖
我們看看這個(gè)結(jié)構(gòu)里邊的各種信息都是干嘛的:

  • 鎖所在的事務(wù)信息:不論是表鎖還是行鎖,都是在事務(wù)執(zhí)行過程中生成的,哪個(gè)事務(wù)生成了這個(gè)鎖結(jié)構(gòu),這里就記載著這個(gè)事務(wù)的信息。

    小提示:
    實(shí)際上這個(gè)所謂的鎖所在的事務(wù)信息在內(nèi)存結(jié)構(gòu)中只是一個(gè)指針而已,所以不會(huì)占用多大內(nèi)存空間,通過指針可以找到內(nèi)存中關(guān)于該事務(wù)的更多信息,比方說事務(wù)id是什么。下邊介紹的所謂的索引信息其實(shí)也是一個(gè)指針

  • 索引信息:對(duì)于行鎖來說,需要記錄一下加鎖的記錄是屬于哪個(gè)索引的。

  • 表鎖/行鎖信息:表鎖結(jié)構(gòu)和行鎖結(jié)構(gòu)在這個(gè)位置的內(nèi)容是不同的

    • 表鎖:記載著這是對(duì)哪個(gè)表加的鎖,還有其他的一些信息。

    • 行鎖:記載了三個(gè)重要的信息

      • Space ID:記錄所在表空間。

      • Page Number:記錄所在頁號(hào)。

      • n_bits:對(duì)于行鎖來說,一條記錄就對(duì)應(yīng)著一個(gè)比特位,一個(gè)頁面中包含很多記錄,用不同的比特位來區(qū)分到底是哪一條記錄加了鎖。為此在行鎖結(jié)構(gòu)的末尾放置了一堆比特位,這個(gè)n_bits屬性代表使用了多少比特位

        小提示:
        并不是該頁面中有多少記錄,n_bits屬性的值就是多少。為了讓之后在頁面中插入了新記錄后也不至于重新分配鎖結(jié)構(gòu),所以n_bits的值一般都比頁面中記錄條數(shù)多一些

  • type_mode:這是一個(gè)32位的數(shù),被分成了lock_modelock_typerec_lock_type三個(gè)部分,如圖所示

    一文帶你了解MySQL之鎖

    • 鎖的模式(lock_mode),占用低4位,可選的值如下:

      • LOCK_IS(十進(jìn)制的0):表示共享意向鎖,也就是IS鎖。

      • LOCK_IX(十進(jìn)制的1):表示獨(dú)占意向鎖,也就是IX鎖。

      • LOCK_S(十進(jìn)制的2):表示共享鎖,也就是S鎖。

      • LOCK_X(十進(jìn)制的3):表示獨(dú)占鎖,也就是X鎖。

      • LOCK_AUTO_INC(十進(jìn)制的4):表示AUTO-INC鎖。

        小提示:
        在InnoDB存儲(chǔ)引擎中,LOCK_IS,LOCK_IX,LOCK_AUTO_INC都算是表級(jí)鎖的模式,LOCK_S和LOCK_X既可以算是表級(jí)鎖的模式,也可以是行級(jí)鎖的模式

    • 鎖的類型(lock_type),占用第5~8位,不過現(xiàn)階段只有第5位和第6位被使用:

      • LOCK_TABLE(十進(jìn)制的16),也就是當(dāng)?shù)?個(gè)比特位置為1時(shí),表示表級(jí)鎖。

      • LOCK_REC(十進(jìn)制的32),也就是當(dāng)?shù)?個(gè)比特位置為1時(shí),表示行級(jí)鎖

    • 行鎖的具體類型(rec_lock_type),使用其余的位來表示。只有在lock_type的值為LOCK_REC時(shí),也就是只有在該鎖為行級(jí)鎖時(shí),才會(huì)被細(xì)分為更多的類型:

      • LOCK_ORDINARY(十進(jìn)制的0):表示next-key鎖

      • LOCK_GAP(十進(jìn)制的512):也就是當(dāng)?shù)?0個(gè)比特位置為1時(shí),表示gap鎖

      • LOCK_REC_NOT_GAP(十進(jìn)制的1024):也就是當(dāng)?shù)?1個(gè)比特位置為1時(shí),表示正經(jīng)記錄鎖

      • LOCK_INSERT_INTENTION(十進(jìn)制的2048):也就是當(dāng)?shù)?2個(gè)比特位置為1時(shí),表示插入意向鎖

      • 其他的類型:還有一些不常用的類型我們就不多說了。

      怎么還沒看見is_waiting屬性呢?這主要還是InnoDB把is_waiting屬性也放到了type_mode這個(gè)32位的數(shù)字中:

      • LOCK_WAIT(十進(jìn)制的256) :也就是當(dāng)?shù)?個(gè)比特位置為1時(shí),表示is_waitingtrue,也就是當(dāng)前事務(wù)尚未獲取到鎖,處在等待狀態(tài);當(dāng)這個(gè)比特位為0時(shí),表示is_waitingfalse,也就是當(dāng)前事務(wù)獲取鎖成功。
  • 其他信息:為了更好的管理系統(tǒng)運(yùn)行過程中生成的各種鎖結(jié)構(gòu)而設(shè)計(jì)了各種哈希表和鏈表,為了簡化討論,我們忽略這部分信息哈~

  • 一堆比特位:如果是行鎖結(jié)構(gòu)的話,在該結(jié)構(gòu)末尾還放置了一堆比特位,比特位的數(shù)量是由上邊提到的n_bits屬性表示的。我們前邊學(xué)習(xí)InnoDB記錄結(jié)構(gòu)的時(shí)候說過,頁面中的每條記錄在記錄頭信息中都包含一個(gè)heap_no屬性,偽記錄Infimumheap_no值為0Supremumheap_no值為1,之后每插入一條記錄,heap_no值就增1。鎖結(jié)構(gòu)最后的一堆比特位就對(duì)應(yīng)著一個(gè)頁面中的記錄,一個(gè)比特位映射一個(gè)heap_no,不過為了編碼方便,映射方式有點(diǎn)怪:

    一文帶你了解MySQL之鎖

    小提示:
    這么怪的映射方式純粹是為了敲代碼方便,大家不要大驚小怪,只需要知道一個(gè)比特位映射到頁內(nèi)的一條記錄就好了

可能上邊的描述大家覺得還是有些抽象,我們還是舉個(gè)例子說明一下。比方說現(xiàn)在有兩個(gè)事務(wù)T1T2想對(duì)hero表中的記錄進(jìn)行加鎖,hero表中記錄比較少,假設(shè)這些記錄都存儲(chǔ)在所在的表空間號(hào)為67,頁號(hào)為3的頁面上,那么如果:

  • T1想對(duì)number值為15的這條記錄加S型正常記錄鎖,在對(duì)記錄加行鎖之前,需要先加表級(jí)別的IS鎖,也就是會(huì)生成一個(gè)表級(jí)鎖的內(nèi)存結(jié)構(gòu),不過我們這里不關(guān)心表級(jí)鎖,所以就忽略掉了哈~ 接下來分析一下生成行鎖結(jié)構(gòu)的過程:

    • 事務(wù)T1要進(jìn)行加鎖,所以鎖結(jié)構(gòu)的鎖所在事務(wù)信息指的就是T1。

    • 直接對(duì)聚簇索引進(jìn)行加鎖,所以索引信息指的其實(shí)就是PRIMARY索引。

    • 由于是行鎖,所以接下來需要記錄的是三個(gè)重要信息:

      • Space ID:表空間號(hào)為67。

      • Page Number:頁號(hào)為3。

      • n_bits:我們的hero表中現(xiàn)在只插入了5條用戶記錄,但是在初始分配比特位時(shí)會(huì)多分配一些,這主要是為了在之后新增記錄時(shí)不用頻繁分配比特位。其實(shí)計(jì)算n_bits有一個(gè)公式:

        n_bits = (1 + ((n_recs + LOCK_PAGE_BITMAP_MARGIN) / 8)) * 8

        其中n_recs指的是當(dāng)前頁面中一共有多少條記錄(算上偽記錄和在垃圾鏈表中的記錄),比方說現(xiàn)在hero表一共有7條記錄(5條用戶記錄和2條偽記錄),所以n_recs的值就是7LOCK_PAGE_BITMAP_MARGIN是一個(gè)固定的值64,所以本次加鎖的n_bits值就是:

        n_bits = (1 + ((7 + 64) / 8)) * 8 = 72

      • type_mode是由三部分組成的:

        • lock_mode,這是對(duì)記錄加S鎖,它的值為LOCK_S。

        • lock_type,這是對(duì)記錄進(jìn)行加鎖,也就是行鎖,所以它的值為LOCK_REC。

        • rec_lock_type,這是對(duì)記錄加正經(jīng)記錄鎖,也就是類型為LOCK_REC_NOT_GAP的鎖。另外,由于當(dāng)前沒有其他事務(wù)對(duì)該記錄加鎖,所以應(yīng)當(dāng)獲取到鎖,也就是LOCK_WAIT代表的二進(jìn)制位應(yīng)該是0。

      綜上所屬,此次加鎖的type_mode的值應(yīng)該是:

      type_mode = LOCK_S | LOCK_REC | LOCK_REC_NOT_GAP

      也就是:

      type_mode = 2 | 32 | 1024 = 1058

    • 其他信息:略~

    • 一堆比特位:因?yàn)?code>number值為15的記錄heap_no值為5,根據(jù)上邊列舉的比特位和heap_no的映射圖來看,應(yīng)該是第一個(gè)字節(jié)從低位往高位數(shù)第6個(gè)比特位被置為1,就像這樣:

      一文帶你了解MySQL之鎖

綜上所述,事務(wù)T1number值為5的記錄加鎖生成的鎖結(jié)構(gòu)就如下圖所示:

一文帶你了解MySQL之鎖

  • T2想對(duì)number值為3、8、15的這三條記錄加X型的next-key鎖,在對(duì)記錄加行鎖之前,需要先加表級(jí)別的IX鎖,也就是會(huì)生成一個(gè)表級(jí)鎖的內(nèi)存結(jié)構(gòu),不過我們這里不關(guān)心表級(jí)鎖,所以就忽略掉了哈~

    現(xiàn)在T2要為3條記錄加鎖,number38的兩條記錄由于沒有其他事務(wù)加鎖,所以可以成功獲取這條記錄的X型next-key鎖,也就是生成的鎖結(jié)構(gòu)的is_waiting屬性為false;但是number15的記錄已經(jīng)被T1加了S型正經(jīng)記錄鎖,T2是不能獲取到該記錄的X型next-key鎖的,也就是生成的鎖結(jié)構(gòu)的is_waiting屬性為true。因?yàn)榈却隣顟B(tài)不相同,所以這時(shí)候會(huì)生成兩個(gè)鎖結(jié)構(gòu)。這兩個(gè)鎖結(jié)構(gòu)中相同的屬性如下:

    • 事務(wù)T2要進(jìn)行加鎖,所以鎖結(jié)構(gòu)的鎖所在事務(wù)信息指的就是T2。

    • 直接對(duì)聚簇索引進(jìn)行加鎖,所以索引信息指的其實(shí)就是PRIMARY索引。

    • 由于是行鎖,所以接下來需要記錄是三個(gè)重要信息:

      • Space ID:表空間號(hào)為67。

      • Page Number:頁號(hào)為3。

      • n_bits:此屬性生成策略同T1中一樣,該屬性的值為72

      • type_mode是由三部分組成的:

        • lock_mode,這是對(duì)記錄加X鎖,它的值為LOCK_X。

        • lock_type,這是對(duì)記錄進(jìn)行加鎖,也就是行鎖,所以它的值為LOCK_REC。

        • rec_lock_type,這是對(duì)記錄加next-key鎖,也就是類型為LOCK_ORDINARY的鎖

    • 其他信息:略~

    不同的屬性如下:

    • number3、8的記錄生成的鎖結(jié)構(gòu):

      • type_mode值:由于可以獲取到鎖,所以is_waiting屬性為false,也就是LOCK_WAIT代表的二進(jìn)制位被置0。所以:

        type_mode = LOCK_X | LOCK_REC |LOCK_ORDINARY

        也就是

        type_mode = 3 | 32 | 0 = 35

      • 一堆比特位:因?yàn)?code>number值為38的記錄heap_no值分別為3、4,根據(jù)上邊列舉的比特位和heap_no的映射圖來看,應(yīng)該是第一個(gè)字節(jié)從低位往高位數(shù)第4、5個(gè)比特位被置為1,就像這樣:

        一文帶你了解MySQL之鎖

      綜上所述,事務(wù)T2為number值為3、8兩條記錄加鎖生成的鎖結(jié)構(gòu)就如下圖所示:

      一文帶你了解MySQL之鎖

    • 為number為15的記錄生成的鎖結(jié)構(gòu):

      • type_mode值:由于不可以獲取到鎖,所以is_waiting屬性為true,也就是LOCK_WAIT代表的二進(jìn)制位被置1。所以:

        type_mode = LOCK_X | LOCK_REC |LOCK_ORDINARY | LOCK_WAIT

        也就是

        type_mode = 3 | 32 | 0 | 256 = 291

      • 一堆比特位: 因?yàn)?code>number值為15的記錄heap_no值為5,根據(jù)上邊列舉的比特位和heap_no的映射圖來看,應(yīng)該是第一個(gè)字節(jié)從低位往高位數(shù)第6個(gè)比特位被置為1,就像這樣:
        一文帶你了解MySQL之鎖

      綜上所述,事務(wù)T2number值為15的記錄加鎖生成的鎖結(jié)構(gòu)就如下圖所示:

      一文帶你了解MySQL之鎖

    綜上所述,事務(wù)T1先獲取number值為15S型正經(jīng)記錄鎖,然后事務(wù)T2獲取number值為38、15X型正經(jīng)記錄鎖共需要生成3個(gè)鎖結(jié)構(gòu)

    小提示:
    上邊事務(wù)T2在對(duì)number值分別為3、8、15這三條記錄加鎖的情景中,是按照先對(duì)number值為3的記錄加鎖、再對(duì)number值為8的記錄加鎖,最后對(duì)number值為15的記錄加鎖的順序進(jìn)行的,如果我們一開始就對(duì)number值為15的記錄加鎖,那么該事務(wù)在為number值為15的記錄生成一個(gè)鎖結(jié)構(gòu)后,直接就進(jìn)入等待狀態(tài),就不為number值為3、8的兩條記錄生成鎖結(jié)構(gòu)了。在事務(wù)T1提交后會(huì)把在number值為15的記錄上獲取的鎖釋放掉,然后事務(wù)T2就可以獲取該記錄上的鎖,這時(shí)再對(duì)number值為3、8的兩條記錄加鎖時(shí),就可以復(fù)用之前為number值為15的記錄加鎖時(shí)生成的鎖結(jié)構(gòu)了。

至此今天的學(xué)習(xí)就到此結(jié)束了,愿您成為堅(jiān)不可摧的自己~~~
?

You can’t connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future.You have to trust in something - your gut, destiny, life, karma, whatever. This approach has never let me down, and it has made all the difference in my life

如果我的內(nèi)容對(duì)你有幫助,請(qǐng) 點(diǎn)贊、評(píng)論、收藏,創(chuàng)作不易,大家的支持就是我堅(jiān)持下去的動(dòng)力!

本文章參考:小孩子《MySQL是怎樣運(yùn)行的》
一文帶你了解MySQL之鎖文章來源地址http://www.zghlxwxcb.cn/news/detail-481556.html

到了這里,關(guān)于一文帶你了解MySQL之鎖的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【MySQL】一文帶你了解數(shù)據(jù)過濾

    【MySQL】一文帶你了解數(shù)據(jù)過濾

    ?? 博客主頁:博主鏈接 ?? 本文由 M malloc 原創(chuàng),首發(fā)于 CSDN?? ?? 學(xué)習(xí)專欄推薦:LeetCode刷題集! ?? 歡迎點(diǎn)贊 ?? 收藏 ?留言 ?? 如有錯(cuò)誤敬請(qǐng)指正! ?? 未來很長,值得我們?nèi)Ρ几案篮玫纳? ??大家好呀,今天是我第N次寫MySQL,也是最近才學(xué)習(xí)MySQL,也想著記錄

    2024年02月09日
    瀏覽(24)
  • 一文帶你了解MySQL之約束

    一文帶你了解MySQL之約束

    在SQL標(biāo)準(zhǔn)中,一共規(guī)定了6種不同的約束, 包括非空約束,唯一約束和檢查約束等,而在MySQL中是不支持檢查約束的,所以這篇文章先對(duì)其余5種約束做一個(gè)詳解和練習(xí)。 約束是作用于表中列上的規(guī)則,用于限制加入表的數(shù)據(jù)。 例如,作為主鍵的列一定是非空的唯一的,否則將

    2024年02月07日
    瀏覽(28)
  • 【MySQL】一文帶你了解SQL

    【MySQL】一文帶你了解SQL

    ?? 博客主頁:博主鏈接 ?? 本文由 M malloc 原創(chuàng),首發(fā)于 CSDN?? ?? 學(xué)習(xí)專欄推薦:LeetCode刷題集! ?? 歡迎點(diǎn)贊 ?? 收藏 ?留言 ?? 如有錯(cuò)誤敬請(qǐng)指正! ?? 未來很長,值得我們?nèi)Ρ几案篮玫纳? ------------------??分割線??------------------------- —————————

    2024年02月08日
    瀏覽(40)
  • 【MySQL】一文帶你了解檢索數(shù)據(jù)

    【MySQL】一文帶你了解檢索數(shù)據(jù)

    ?? 博客主頁:博主鏈接 ?? 本文由 M malloc 原創(chuàng),首發(fā)于 CSDN?? ?? 學(xué)習(xí)專欄推薦:LeetCode刷題集! ?? 歡迎點(diǎn)贊 ?? 收藏 ?留言 ?? 如有錯(cuò)誤敬請(qǐng)指正! ?? 未來很長,值得我們?nèi)Ρ几案篮玫纳? ------------------??分割線??------------------------- —————————

    2024年02月09日
    瀏覽(20)
  • 【MySQL】一文帶你了解過濾數(shù)據(jù)

    【MySQL】一文帶你了解過濾數(shù)據(jù)

    ?? 博客主頁:博主鏈接 ?? 本文由 M malloc 原創(chuàng),首發(fā)于 CSDN?? ?? 學(xué)習(xí)專欄推薦:LeetCode刷題集! ?? 歡迎點(diǎn)贊 ?? 收藏 ?留言 ?? 如有錯(cuò)誤敬請(qǐng)指正! ?? 未來很長,值得我們?nèi)Ρ几案篮玫纳? ??大家好呀,今天是我第N次寫MySQL,也是最近才學(xué)習(xí)MySQL,也想著記錄

    2024年02月09日
    瀏覽(26)
  • 一文帶你了解MySQL之連接原理

    一文帶你了解MySQL之連接原理

    前言 我們搞數(shù)據(jù)庫一個(gè)都避不開的概念就是 連接 ( join )。相信很多小伙伴初學(xué)連接的時(shí)候有些一臉懵,理解了連接的語義之后又可能搞不明白各個(gè)表中的記錄到底是怎么連起來的,以至于在后期使用數(shù)據(jù)庫的時(shí)候常常陷入下邊兩種誤區(qū): 誤區(qū)一 :業(yè)務(wù)至上,不管三七二

    2024年02月05日
    瀏覽(24)
  • 【MySQL】一文帶你了解MySQL中的子查詢

    【MySQL】一文帶你了解MySQL中的子查詢

    子查詢指一個(gè)查詢語句嵌套在另一個(gè)查詢語句內(nèi)部的查詢,這個(gè)特性從MySQL 4.1開始引入 。 SQL 中子查詢的使用大大增強(qiáng)了 SELECT 查詢的能力 ,因?yàn)楹芏鄷r(shí)候查詢需要從結(jié)果集中獲取數(shù)據(jù),或者需要從同一個(gè)表中先計(jì)算得出一個(gè)數(shù)據(jù)結(jié)果,然后與這個(gè)數(shù)據(jù)結(jié)果(可能是某個(gè)標(biāo)量

    2024年02月08日
    瀏覽(27)
  • 【MySQL】一文帶你了解MySQL的基礎(chǔ)知識(shí)

    【MySQL】一文帶你了解MySQL的基礎(chǔ)知識(shí)

    ?? 博客主頁:博主鏈接 ?? 本文由 M malloc 原創(chuàng),首發(fā)于 CSDN?? ?? 學(xué)習(xí)專欄推薦:LeetCode刷題集! ?? 歡迎點(diǎn)贊 ?? 收藏 ?留言 ?? 如有錯(cuò)誤敬請(qǐng)指正! ?? 未來很長,值得我們?nèi)Ρ几案篮玫纳? ------------------??分割線??------------------------- —————————

    2024年02月08日
    瀏覽(22)
  • 一文了解MySQL中的多版本并發(fā)控制

    作者:京東零售??李澤陽 最近在閱讀《認(rèn)知覺醒》這本書,里面有句話非常打動(dòng)我: 通過自己的語言,用最簡單的話把一件事情講清楚,最好讓外行人也能聽懂。 也許這就是大道至簡,只是我們習(xí)慣了煩瑣和復(fù)雜。 希望借助今天這篇文章,能用大白話說清楚這個(gè)相對(duì)比較

    2023年04月11日
    瀏覽(26)
  • 【MySQL】一文帶你了解數(shù)據(jù)庫約束

    【MySQL】一文帶你了解數(shù)據(jù)庫約束

    作用: 數(shù)據(jù)庫約束是用于限制數(shù)據(jù)輸入或數(shù)據(jù)更新的規(guī)則。數(shù)據(jù)庫約束確保在表中只能插入符合規(guī)則的數(shù)據(jù),以確保數(shù)據(jù)的完整性和一致性。 常見的數(shù)據(jù)庫約束包括: 主鍵約束:用于唯一標(biāo)識(shí)表中的每一行數(shù)據(jù),確保數(shù)據(jù)唯一性和完整性。 外鍵約束:用于確保表之間數(shù)據(jù)的

    2024年02月08日
    瀏覽(92)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包