詳解MySql中InnoDB存儲引擎中的各種鎖
什么是鎖
現(xiàn)實生活中的鎖是為了保護你的私有物品,在數(shù)據(jù)庫中鎖是為了解決資源爭搶的問題,鎖是數(shù)據(jù)庫系統(tǒng)區(qū)別于文件系統(tǒng)的一個關(guān)鍵特性。鎖機制用于管理對共享資源的并發(fā)訪。
數(shù)據(jù)庫系統(tǒng)使用鎖是為了支持對共享資源進行并發(fā)訪問,提供數(shù)據(jù)的完整性和一致性
InnoDB存儲引擎區(qū)別于MyISAM的兩個重要特征就是:InnoDB存儲引擎支持事務(wù)和行級別的鎖,MyISAM只支持表級別的鎖
InnoDB存儲引擎中的鎖
InnoDB存儲引擎實現(xiàn)了如下兩種標準的行級鎖:
共享鎖(S Lock),允許事務(wù)讀一行數(shù)據(jù)排他鎖(X Lock),允許事務(wù)刪除或更新一行數(shù)據(jù)
鎖的兼容性
- | X | S |
---|---|---|
X | 不兼容 | 不兼容 |
S | 不兼容 | 兼容 |
可以看到,X排他鎖不與其他鎖兼容,S共享鎖只與S兼容
此外,InnoDB存儲引擎支持多粒度(granular)鎖定,這種鎖定允許事務(wù)在行級上的鎖和表級上的鎖同時存在
為了支持在不同粒度上進行加鎖操作,InnoDB存儲引擎支持一種額外的鎖方式,稱之為意向鎖(Intention Lock)。
意向鎖是將鎖定的對象分為多個層次,意向鎖意味著事務(wù)希望在更細粒度(fine granularity)上進行加鎖
如上圖,數(shù)據(jù)庫從上到下可以分為數(shù)據(jù)庫、表、頁、記錄四個層次,行記錄是最細粒度的鎖,我們在獲取行鎖的時候,需要從上到下各個級別分別進行鎖定,最后才能獲取到行鎖。比如,你要獲取行記錄x的鎖,需要先在數(shù)據(jù)庫、表、頁上加意向鎖IX,其中任何一方需要等待鎖,會造成行鎖的等待
InnoDB存儲引擎支持的意向鎖即為表級別的鎖。支持兩種意向鎖
- 意向共享鎖(IS Lock),事務(wù)想要獲得一張表中某幾行的共享鎖
- 意向排他鎖(IX Lock),事務(wù)想要獲得一張表中某幾行的排他鎖
由于InnoDB存儲引擎支持的是行級別的鎖,因此意向鎖其實不會阻塞除全表掃以外的任何請求。故表級意向鎖與行級鎖的兼容性如表所示
- | IS | IX | S | X |
---|---|---|---|---|
IS | 兼容 | 兼容 | 兼容 | 不兼容 |
IX | 兼容 | 兼容 | 不兼容 | 不兼容 |
S | 兼容 | 不兼容 | 兼容 | 不兼容 |
X | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
一致性非鎖定讀
一致性的非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎通過行多版本控制(multi versioning)的方式來讀取當前執(zhí)行時間數(shù)據(jù)庫中行的數(shù)據(jù)。
如果讀取的行正在執(zhí)行DELETE或UPDATE操作,這時讀取操作不會因此去等待行上鎖的釋放。
相反地,InnoDB存儲引擎會去讀取行的一個快照數(shù)據(jù)
快照數(shù)據(jù)是指該行的之前版本的數(shù)據(jù),該實現(xiàn)是通過undo段來完成。而undo用來在事務(wù)中回滾數(shù)據(jù),因此快照數(shù)據(jù)本身是沒有額外的開銷。此外,讀取快照數(shù)據(jù)是不需要上鎖的,因為沒有事務(wù)需要對歷史的數(shù)據(jù)進行修改操作
快照數(shù)據(jù)其實就是當前行數(shù)據(jù)之前的歷史版本,每行記錄可能有多個版本。如上圖,記錄B就有多個歷史的快照版本
這就是大名鼎鼎的MVCC
多版本并發(fā)控制(Multi Version Concurrency Control,MVCC)是指一個行記錄有多個快照版本,由多個快照版本引發(fā)的并發(fā)控制,叫做多版本并發(fā)控制
那這么多歷史的快速版本,訪問的時候該用哪一個呢?
- 在READ COMMITTED事務(wù)隔離級別下,非一致性讀總是讀取被鎖定行的最新一份快照數(shù)據(jù)
- 在REPEATABLE READ事務(wù)隔離級別下,非一致性讀總是讀取事務(wù)開始時的行數(shù)據(jù)版本
由此可見,不同的事務(wù)隔離級別在MVCC的處理上還不一樣
一致性鎖定讀
在默認配置下,即事務(wù)的隔離級別為REPEATABLE READ模式下,InnoDB存儲引擎的SELECT操作使用一致性非鎖定讀
但是在某些情況下,用戶需要顯式地對數(shù)據(jù)庫讀取操作進行加鎖以保證數(shù)據(jù)邏輯的一致性
InnoDB存儲引擎對于SELECT語句支持兩種一致性的鎖定讀(locking read)操作:
- SELECT…FOR UPDATE 對讀取的行記錄加一個X鎖,其他事務(wù)不能對已鎖定的行加上任何鎖
- SELECT…LOCK IN SHARE MODE對讀取的行記錄加一個S鎖,其他事務(wù)可以向被鎖定的行加S鎖,但是如果加X鎖,則會被阻塞
對于一致性非鎖定讀,即使讀取的行已被執(zhí)行了SELECT…FOR UPDATE,也是可以進行讀取的
一致性鎖定讀則需要檢查被讀取的行上有沒有互斥的鎖,假如有互斥的鎖存在就需要等待鎖的釋放
鎖的算法
行鎖的3種算法
- Record Lock:單個行記錄上的鎖
- Gap Lock:間隙鎖,鎖定一個范圍,但不包含記錄本身
- Next-Key Lock∶Gap Lock+Record Lock,鎖定一個范圍,并且鎖定記錄本身
Record Lock總是會去鎖住索引記錄,如果InnoDB存儲引擎表在建立的時候沒有設(shè)置任何一個索引,那么這時InnoDB存儲引擎會使用隱式的主鍵來進行鎖定
Gap Lock的作用是為了阻止多個事務(wù)將記錄插入到同一范圍內(nèi),而這會導(dǎo)致幻讀問題的產(chǎn)生
用戶可以通過以下兩種方式來顯式地關(guān)閉Gap Lock:
- 將事務(wù)的隔離級別設(shè)置為READ COMMITTED
- 將參數(shù)innodb_locks_unsafe_for_binlog設(shè)置為1
在上述的配置下,除了外鍵約束和唯一性檢查依然需要的Gap Lock,其余情況僅使用RecordLock進行鎖定。
但需要牢記的是,上述設(shè)置破壞了事務(wù)的隔離性,并且對于replication,可能會導(dǎo)致主從數(shù)據(jù)的不一致。
Next-Key Lock是結(jié)合了Gap Lock和Record Lock的一種鎖定算法,在Next-Key Lock算法下,InnoDB對于行的查詢都是采用這種鎖定算法
當查詢的索引含有唯一屬性時,InnoDB存儲引擎會對Next-Key Lock進行優(yōu)化,將其降級為Record Lock,即僅鎖住索引本身,而不是范圍
幻像問題
幻像問題(Phantom Problem)是指在同一事務(wù)下,連續(xù)執(zhí)行兩次同樣的SQL語句可能導(dǎo)致不同的結(jié)果,第二次的SQL語句可能會返回之前不存在的行
看下面的場景:
- 表t由1、2、5這三個值組成
- 執(zhí)行select * from t where a > 2 for update;
- 上述事務(wù)T1并沒有提交,那么此時另一個事務(wù)T2插入4這個值,并且數(shù)據(jù)庫允許這個操作
- 那么事務(wù)T1再執(zhí)行上述查詢,就得到4、5兩筆記錄,跟第一次得到的結(jié)果不一樣,違反了事務(wù)的隔離性
InnoDB存儲引擎采用Next-Key Locking的算法避免幻像問題。對于上述的SQL語句select * from t where a > 2 for update,其鎖住的不是5這單個值,而是對(2,+∞)這個范圍加了X鎖。因此任何對于這個范圍的插入都是不被允許的,從而避免幻像問題
InnoDB存儲引擎默認的事務(wù)隔離級別是REPEATABLE READ,在該隔離級別下,其采用Next-Key Locking的方式來加鎖
而在事務(wù)隔離級別READ COMMITTED下,其僅采用RecordLock行鎖
鎖的問題
臟讀
臟讀指的就是在不同的事務(wù)下,當前事務(wù)可以讀到另外事務(wù)未提交的數(shù)據(jù),簡單來說就是可以讀到臟數(shù)據(jù)
臟讀發(fā)生的條件是需要事務(wù)的隔離級別為READ UNCOMMITTED,而目前絕大部分的數(shù)據(jù)庫都至少設(shè)置成READCOMMITTED。
InnoDB存儲引擎默認的事務(wù)隔離級別為READ REPEATABLE,Microsoft SQLServer數(shù)據(jù)庫為READ COMMITTED,Oracle數(shù)據(jù)庫同樣也是READ COMMITTED
不可重復(fù)讀
不可重復(fù)讀是指在一個事務(wù)內(nèi)多次讀取同一數(shù)據(jù)集合。在這個事務(wù)還沒有結(jié)束時,另外一個事務(wù)也訪問該同一數(shù)據(jù)集合,并做了一些DML操作。因此,在第一個事務(wù)中的兩次讀數(shù)據(jù)之間,由于第二個事務(wù)的修改,那么第一個事務(wù)兩次讀到的數(shù)據(jù)可能是不一樣的。這樣就發(fā)生了在一個事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的情況,這種情況稱為不可重復(fù)讀
不可重復(fù)讀和臟讀的區(qū)別是:臟讀是讀到未提交的數(shù)據(jù),而不可重復(fù)讀讀到的卻是已經(jīng)提交的數(shù)據(jù),但是其違反了數(shù)據(jù)庫事務(wù)一致性的要求
一般來說,不可重復(fù)讀的問題是可以接受的,因為其讀到的是已經(jīng)提交的數(shù)據(jù),本身并不會帶來很大的問題。因此,很多數(shù)據(jù)庫廠商(如Oracle、Microsoft SQL Server)將其數(shù)據(jù)庫事務(wù)的默認隔離級別設(shè)置為READ COMMITTED,在這種隔離級別下允許不可重復(fù)讀的現(xiàn)象
在InnoDB存儲引擎中,通過使用Next-Key Lock算法來避免不可重復(fù)讀的問題。
在MySQL官方文檔中將不可重復(fù)讀的問題定義為Phantom Problem,即幻像問題。在Next-Key Lock算法下,對于索引的掃描,不僅是鎖住掃描到的索引,而且還鎖住這些索引覆蓋的范圍(gap)。
因此在這個范圍內(nèi)的插入都是不允許的。這樣就避免了另外的事務(wù)在這個范圍內(nèi)插入數(shù)據(jù)導(dǎo)致的不可重復(fù)讀的問題。因此,InnoDB存儲引擎的默認事務(wù)隔離級別是READ REPEATABLE,采用Next-Key Lock算法,避免了不可重復(fù)讀的現(xiàn)象
丟失更新
丟失更新是另一個鎖導(dǎo)致的問題,簡單來說其就是一個事務(wù)的更新操作會被另一個事務(wù)的更新操作所覆蓋,從而導(dǎo)致數(shù)據(jù)的不一致
- 事務(wù)T1將行記錄r更新為v1,但是事務(wù)T1并未提交
- 與此同時,事務(wù)T2將行記錄r更新為v2,事務(wù)T2未提交
- 事務(wù)T1提交
- 事務(wù)T2提交
在當前數(shù)據(jù)庫的任何隔離級別下,都不會導(dǎo)致數(shù)據(jù)庫理論意義上的丟失更新問題。這是因為,即使是READ UNCOMMITTED的事務(wù)隔離級別,對于行的DML操作,需要對行或其他粗粒度級別的對象加鎖
死鎖
死鎖是指兩個或兩個以上的事務(wù)在執(zhí)行過程中,因爭奪鎖資源而造成的一種互相等待的現(xiàn)象
解決死鎖問題最簡單的一種方法是超時,即當兩個事務(wù)互相等待時,當一個等待時間超過設(shè)置的某一閾值時,其中一個事務(wù)進行回滾,另一個等待的事務(wù)就能繼續(xù)進行。在InnoDB存儲引擎中,參數(shù)innodb_lock_wait_timeout用來設(shè)置超時的時間
超時機制雖然簡單,但是其僅通過超時后對事務(wù)進行回滾的方式來處理,或者說其是根據(jù)FIFO的順序選擇回滾對象。但若超時的事務(wù)所占權(quán)重比較大,如事務(wù)操作更新了很多行,占用了較多的undo log,這時采用FIFO的方式,就顯得不合適了,因為回滾這個事務(wù)的時間相對另一個事務(wù)所占用的時間可能會很多
因此,除了超時機制,當前數(shù)據(jù)庫還都普遍采用wait-for graph(等待圖)的方式來進行死鎖檢測。
較之超時的解決方案,這是一種更為主動的死鎖檢測方式。InnoDB存儲引擎也采用的這種方式。
wait-for graph要求數(shù)據(jù)庫保存以下兩種信息
- 鎖的信息鏈表
- 事務(wù)等待鏈表
wait-for graph的死鎖檢測通常采用深度優(yōu)先的算法實現(xiàn),通常來說InnoDB存儲引擎選擇回滾undo量最小的事務(wù)
到此這篇關(guān)于詳解MySql中InnoDB存儲引擎中的各種鎖的文章就介紹到這了,更多相關(guān)詳解MySql中InnoDB存儲引擎中的各種鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mysql?InnoDB引擎中的數(shù)據(jù)頁結(jié)構(gòu)詳解
這篇文章主要為大家介紹了Mysql?InnoDB引擎中的數(shù)據(jù)頁結(jié)構(gòu)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05MySQL?原理優(yōu)化之Group?By的優(yōu)化技巧
這篇文章主要介紹了MySQL?原理優(yōu)化之Group?By的優(yōu)化技巧,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-08-08MySQL數(shù)據(jù)庫可以用任意ip連接訪問的方法
本文主要介紹了MySQL數(shù)據(jù)庫可以用任意ip連接訪問的方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05Mysql賬號管理與引擎相關(guān)功能實現(xiàn)流程
Mysql中的每一種技術(shù)都使用不同的存儲機制、索引技巧、鎖定水平、并且最終提供廣泛的不同功能和能力。通過選擇不同的技術(shù),你能夠獲得額外的速度或者功能,從而改善應(yīng)用的整體功能。這些不同的技術(shù)以及配套的相關(guān)功能在MySQL中被稱作存儲引擎2022-10-10SQL優(yōu)化老出錯,那是你沒弄明白MySQL解釋計劃用法
本篇文章講的是SQL優(yōu)化老出錯,那是你沒弄明白MySQL解釋計劃用法,有興趣的小伙伴速度來看看吧,希望本篇文章能夠幫助到你2021-11-11