十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊
量身定制 + 運(yùn)營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
這篇文章為大家?guī)碛嘘P(guān)MySQL事務(wù)和InnoDB鎖類型的介紹。文章涵蓋MySQL事務(wù)和InnoDB鎖類型的知識點,希望大家通過這篇文章能有所收獲。
創(chuàng)新互聯(lián)建站一直在為企業(yè)提供服務(wù),多年的磨煉,使我們在創(chuàng)意設(shè)計,網(wǎng)絡(luò)營銷推廣到技術(shù)研發(fā)擁有了開發(fā)經(jīng)驗。我們擅長傾聽企業(yè)需求,挖掘用戶對產(chǎn)品需求服務(wù)價值,為企業(yè)制作有用的創(chuàng)意設(shè)計體驗。核心團(tuán)隊擁有超過十載以上行業(yè)經(jīng)驗,涵蓋創(chuàng)意,策化,開發(fā)等專業(yè)領(lǐng)域,公司涉及領(lǐng)域有基礎(chǔ)互聯(lián)網(wǎng)服務(wù)服務(wù)器托管、成都app開發(fā)、手機(jī)移動建站、網(wǎng)頁設(shè)計、網(wǎng)絡(luò)整合營銷。
一、事務(wù)的隔離級別
(1)未提交讀(Read uncommitted):一個事務(wù)讀取到其他事務(wù)未提交的數(shù)據(jù),是級別最低的隔離機(jī)制;
(2)提交讀(Read committed):一個事務(wù)讀取到其他事務(wù)提交后的數(shù)據(jù);
(3)可重復(fù)讀(Repeatable read):一個事務(wù)對同一份數(shù)據(jù)讀取到的相同,不在乎其他事務(wù)對數(shù)據(jù)的修改;
(4)序列化(Serializable) :事務(wù)串行化執(zhí)行,隔離級別最高,犧牲了系統(tǒng)的并發(fā)性。
若不考慮事務(wù)的隔離級別,則事務(wù)的并發(fā)會造成以下問題:
(1)臟讀:事務(wù)A讀取了事務(wù)B更新的數(shù)據(jù),然后B回滾操作,那么A讀取到的數(shù)據(jù)是臟數(shù)據(jù)。
(2)不可重復(fù)讀:事務(wù) A 多次讀取同一數(shù)據(jù),事務(wù) B 在事務(wù)A多次讀取的過程中,對數(shù)據(jù)作了更新并提交,導(dǎo)致事務(wù)A多次讀取同一數(shù)據(jù)時,結(jié)果 不一致。
(3)幻讀:同一事務(wù)中對同一范圍的數(shù)據(jù)進(jìn)行讀取,結(jié)果卻多出了數(shù)據(jù)或者少了數(shù)據(jù),這就叫幻讀。(如同一事務(wù)對id<10的范圍進(jìn)行2次查詢,第一次出現(xiàn)id=8、9的兩條數(shù)據(jù),第二次出現(xiàn)id=7、8、9的3條數(shù)據(jù))。
不可重復(fù)讀的和幻讀很容易混淆,不可重復(fù)讀側(cè)重于修改,幻讀側(cè)重于新增或刪除。解決不可重復(fù)讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。
不同的隔離級別針對上述3個問題的解決能力,如下表:
上文提到 InnoDB 默認(rèn)的隔離級別是可重復(fù)讀(RR),InnoDB是通過MVCC(多版本并發(fā)控制)來實現(xiàn)可重復(fù)讀的,下面為大家介紹MVCC。
在InnoDB中,給每行增加兩個隱藏字段來實現(xiàn)MVCC,一個用來記錄數(shù)據(jù)行的創(chuàng)建時間,另一個用來記錄行的過期時間(刪除時間)。在實際操作中,存儲的并不是時間,而是事務(wù)的版本號,每開啟一個新事務(wù),事務(wù)的版本號就會遞增。
于是乎,默認(rèn)的隔離級別(REPEATABLE READ)下,增刪查改變成了這樣:
(1)SELECT
(2)INSERT
(3)UPDATE
(4)DELETE
(1)快照讀:讀取的是快照版本,也就是歷史版本;
(2)當(dāng)前讀:讀取的是最新版本。
普通的SELECT就是快照讀,而UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE是當(dāng)前讀。
(3)結(jié)論:如果隔離級別是REPEATABLE READ,那么在同一個事務(wù)中的所有普通select讀讀到的都是事務(wù)第一個讀到的快照,如此實現(xiàn)了可重復(fù)讀;而對于當(dāng)前讀(UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE),InnoDB 通過加鎖來實現(xiàn)可重復(fù)讀,且InnoDB 加鎖同時解決了幻讀問題。
InnoDB 引入以下三種鎖類型:
Record Locks(記錄鎖):在索引記錄上加鎖,即行鎖,鎖住當(dāng)前行。
Gap Locks(間隙鎖):在索引記錄之間加鎖,或者在第一個索引記錄之前加鎖,或者在最后一個索引記錄之后加鎖。
假設(shè)一個索引包含以下幾個值:10,11,13,20。那么這個索引的next-key鎖將會覆蓋以下區(qū)間:(-oo, 10]、(10, 11]、(11, 13]、(13, 20]、(20, +oo)。
MySQL InnoDB 通過間隙鎖解決了幻讀問題。以下通過實際的案例分析來介紹InnoDB 是如果解決幻讀問題的。
在對SQL進(jìn)行加鎖分析前,需要明確表的結(jié)構(gòu)和索引類型。在不知道索引的情況下直接給出一條SQL來分析如果加鎖是沒有任何意義的。
以下以用戶表(t_user)為例(id為主鍵,name為唯一索引,age為一般索引,address無索引)分析不同索引條件的加鎖表現(xiàn)。
例:delete from t_user where id=120;
條件為主鍵,此時鎖住聚簇索引中對應(yīng)的行記錄:即Record Locks鎖住id=120的行記錄。
此種情況下,其他事務(wù)除了不能刪除、更新此條記錄外,其他插入其他行、更新其他行都行。
SQL驗證:
例:delete from t_user where name='n20';
條件為唯一索引,鎖住索引記錄,同時鎖住聚簇索引中的對應(yīng)行記錄:
SQL驗證:
例:delete from t_user where age=20;
與主鍵和唯一索引不同的是,一般索引的記錄是允許重復(fù)的;換句話說,如果我們單純地給索引加記錄鎖時,其他事務(wù)依然可以插入,也就有可能出現(xiàn)幻讀問題了。
所以除了給對應(yīng)索引記錄加上記錄鎖之外,還要給Gap加上鎖。
從上面知識點我們可以預(yù)估這個操作一共需要的鎖:
age索引記錄鎖(Record Lock) :
20_120, 20_130(以下均用age_id這種形式表示索引值)
age索引間隙鎖(Gap X-Lock):
(10, 20)、(20, 20)、(20, 40)
聚簇索引上的記錄鎖(Record X-Lock):
id=120/130對應(yīng)的行記錄
SQL驗證:
根據(jù)實際情況,3-6均符合我們預(yù)期,然而7和8則超出了我們預(yù)期的鎖范圍。為什么會超出我們預(yù)期呢?此次我們進(jìn)行分析一下:
從7、8插入語句來看,由于id為自增主鍵,會自動遞增,語句7插入值預(yù)計為:10_141;
語句8插入值預(yù)計為:40_141,為什么只有后者能插入呢?
其實我們可以將B+樹中的間隙理解得更加精準(zhǔn)一點:
age=20的三個間隙應(yīng)該為:(10_110, 20_120)、(20_120, 20_130)、(20_130, 40_140);
從上圖可以看出語句7插入值10_141 無法插入,因為間隙被鎖住了;而語句8插入 40_141值因為在間隙之外了,無鎖沖突,允許插入。
所以最終的加鎖情況應(yīng)該這樣表示:
age索引記錄鎖(Record Lock) :20_120, 20_130
age索引間隙鎖(Gap X-Lock):(10_110, 20_120)、(20_120, 20_130)、(20_130, 40_140)
delete from t_user where address='a20',因為無法精準(zhǔn)定位,InnoDB選擇將聚簇索引中的所有行以及間隙都鎖起來,功能上已經(jīng)等于鎖表了:
SQL驗證:
InnoDB 在RC(READ COMMITTED)隔離級別中,只會在對應(yīng)的索引/行記錄上加Record Lock,而不會加Gap鎖,原因也很簡單,因為該隔離級別是允許存在幻讀問題的。
在RR級別下的加鎖方式稱之為Next-Key Locks,其實就是上述Record Locks和Gap Locks的結(jié)合。比如Gap Lock為(10,20) ,record lock為20,結(jié)合的Next-Key lock 為:(10, 20]。
分析Next-Key Locks其實就是要分析Record Locks和Gap Locks。MySQL InnoDB的可重復(fù)讀并不保證避免幻讀,需要應(yīng)用使用加鎖讀來保證。而這個加鎖讀使用到的機(jī)制就是next-key locks。
如果使用普通的讀,會得到一致性的結(jié)果,如果使用了加鎖的讀,就會讀到“最新的”“提交”讀的結(jié)果。本身,可重復(fù)讀和提交讀是矛盾的。在同一個事務(wù)里,如果保證了可重復(fù)讀,
就會看不到其他事務(wù)的提交,違背了提交讀;如果保證了提交讀,就會導(dǎo)致前后兩次讀到的結(jié)果不一致,違背了可重復(fù)讀??梢赃@么講,InnoDB提供了這樣的機(jī)制,在默認(rèn)的可重復(fù)讀的隔離級別里,可以使用加鎖讀去查詢最新的數(shù)據(jù)。
看完上述內(nèi)容,你們對MySQL事務(wù)和InnoDB鎖類型有進(jìn)一步的了解嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!