1.數(shù)據(jù)庫鎖的分類
本圖源自CSDN博主:Stephen.W
數(shù)據(jù)庫鎖一般可以分為兩類,一個是悲觀鎖,一個是樂觀鎖
樂觀鎖一般是指用戶自己實現(xiàn)的一種鎖機制,假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會造成沖突,所以在數(shù)據(jù)進行提交更新的時候,才會正式對數(shù)據(jù)的沖突與否進行檢測,如果發(fā)現(xiàn)沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。樂觀鎖的實現(xiàn)方式一般包括使用版本號和時間戳 (也就是在數(shù)據(jù)庫中添加了版本號和時間戳字段,以便檢測)
悲觀鎖一般就是我們通常說的數(shù)據(jù)庫鎖機制,以下討論都是基于悲觀鎖
悲觀鎖主要表鎖、行鎖、頁鎖。在MyISAM中只用到表鎖,不會有死鎖的問題,鎖的開銷也很小,但是相應(yīng)的并發(fā)能力很差。innodb實現(xiàn)了行級鎖和表鎖,鎖的粒度變小了,并發(fā)能力變強,但是相應(yīng)的鎖的開銷變大,很有可能出現(xiàn)死鎖。同時innodb需要協(xié)調(diào)這兩種鎖,算法也變得復(fù)雜。InnoDB行鎖是通過給索引上的索引項加鎖來實現(xiàn)的,只有通過索引條件檢索數(shù)據(jù),InnoDB才使用行級鎖,否則,InnoDB將使用表鎖
表鎖和行鎖都分為共享鎖和排他鎖,而更新鎖是為了解決行鎖升級(共享鎖升級為獨占鎖)的死鎖問題
innodb中表鎖和行鎖一起用,所以為了提高效率才會有意向鎖 (意向共享鎖和意向排他鎖)
2.行鎖
共享鎖(讀鎖S鎖)
共享鎖允許其他事務(wù)讀,但是不允許寫??
加鎖與解鎖: 當(dāng)一個事務(wù)執(zhí)行select語句時,數(shù)據(jù)庫系統(tǒng)會為這個事務(wù)分配一把共享鎖,來鎖定被查詢的數(shù)據(jù)。在默認(rèn)情況下,數(shù)據(jù)被讀取后,數(shù)據(jù)庫系統(tǒng)立即解除共享鎖。例如,當(dāng)一個事務(wù)執(zhí)行查詢“SELECT * FROM accounts
”語句時,數(shù)據(jù)庫系統(tǒng)首先鎖定第一行,讀取之后,解除對第一行的鎖定,然后鎖定第二行。這樣,在一個事務(wù)讀操作過程中,允許其他事務(wù)同時更新accounts表中未鎖定的行。
兼容性: 如果數(shù)據(jù)資源上放置了共享鎖,還能再放置共享鎖和更新鎖
并發(fā)性能: 具有良好的并發(fā)性能,當(dāng)數(shù)據(jù)被放置共享鎖后,還可以再放置共享鎖或更新鎖。所以并發(fā)性能很好。
排他鎖(寫鎖X鎖)
排他鎖不允許其他事務(wù)讀和寫??
加鎖與解鎖: 當(dāng)一個事務(wù)執(zhí)行insert、update或delete語句
時,數(shù)據(jù)庫系統(tǒng)會自動對SQL語句操縱的數(shù)據(jù)資源使用獨占鎖(即排他鎖)
兼容性: 獨占鎖不能和其他鎖兼容,如果數(shù)據(jù)資源上已經(jīng)加了獨占鎖,就不能再放置其他的鎖了。同樣,如果數(shù)據(jù)資源上已經(jīng)放置了其他鎖,那么也就不能再放置獨占鎖了
并發(fā)性能: 最差。只允許一個事務(wù)訪問鎖定的數(shù)據(jù),如果其他事務(wù)也需要訪問該數(shù)據(jù),就必須等待
更新鎖
更新鎖在的初始化階段用來鎖定可能要被修改的資源,這可以避免使用共享鎖造成的死鎖現(xiàn)象。例如,對于以下的update
語句:
UPDATE accounts SET balance=900 WHERE id=1
更新操作需要分兩步:讀取accounts表中id為1的記錄 –> 執(zhí)行更新操作
那么什么情況下會造成死鎖現(xiàn)象呢:
如果在第一步使用共享鎖,再第二步把鎖升級為獨占鎖,就可能出現(xiàn)死鎖現(xiàn)象。例如:兩個事務(wù)都獲取了同一數(shù)據(jù)資源的共享鎖,然后都要把鎖升級為獨占鎖,但需要等待另一個事務(wù)解除共享鎖才能升級為獨占鎖,這就造成了死鎖??
更新鎖有如下特征:
加鎖與解鎖: 當(dāng)一個事務(wù)執(zhí)行update
語句時,數(shù)據(jù)庫系統(tǒng)會先為事務(wù)分配一把更新鎖。當(dāng)讀取數(shù)據(jù)完畢,執(zhí)行更新操作時,會把更新鎖升級為獨占鎖
兼容性: 更新鎖與共享鎖是兼容的,也就是說,一個資源可以同時放置更新鎖和共享鎖,但是最多放置一把更新鎖。這樣,當(dāng)多個事務(wù)更新相同的數(shù)據(jù)時,只有一個事務(wù)能獲得更新鎖,然后再把更新鎖升級為獨占鎖,其他事務(wù)必須等到前一個事務(wù)結(jié)束后,才能獲取得更新鎖,這就避免了死鎖
并發(fā)性能: 允許多個事務(wù)同時讀鎖定的資源,但不允許其他事務(wù)修改它
3.意向鎖(IX/IS鎖)
innodb中表鎖和行鎖一起用,所以為了提高效率才會有意向鎖(意向共享鎖和意向排他鎖)
- 在mysql中有表鎖,讀鎖鎖表,會阻塞其他事務(wù)寫表數(shù)據(jù)。寫鎖鎖表,會阻塞其他事務(wù)讀和寫表數(shù)據(jù)
- Innodb引擎又支持行鎖,行鎖分為共享鎖,一個事務(wù)對一行的共享只讀鎖。排它鎖,一個事務(wù)對一行的排他讀寫鎖
- 這兩中類型的鎖共存的問題考慮這個例子:事務(wù)A鎖住了表中的一行,讓這一行只能讀,不能寫。之后,事務(wù)B申請整個表的寫鎖。如果事務(wù)B申請成功,那么理論上它就能修改表中的任意一行,這與A持有的行鎖是沖突的。數(shù)據(jù)庫需要避免這種沖突,就是說要讓B的申請被阻塞,直到A釋放了行鎖
數(shù)據(jù)庫要怎么判斷這個沖突呢?
- 判斷表是否已被其他事務(wù)用表鎖鎖表
- 判斷表中的每一行是否已被行鎖鎖住
判斷表中的每一行是否已被行鎖鎖住。這樣的判斷方法效率實在不高,因為需要遍歷整個表。于是就有了意向鎖。在意向鎖存在的情況下,事務(wù)A必須先申請表的意向共享鎖,成功后再申請一行的行鎖??
在意向鎖存在的情況下,上面的判斷可以改成
- 判斷表是否已被其他事務(wù)用表鎖鎖表
- 發(fā)現(xiàn)表上有意向共享鎖,說明表中有些行被共享行鎖鎖住了,因此,事務(wù)B申請表的寫鎖會被阻塞
申請意向鎖的動作是數(shù)據(jù)庫完成的,就是說,事務(wù)A申請一行的行鎖的時候,數(shù)據(jù)庫會自動先開始申請表的意向鎖,不需要我們程序員使用代碼來申請??
4.鎖機制解釋數(shù)據(jù)庫隔離級別
每一種隔離級別滿足不同的數(shù)據(jù)要求,使用不同程度的鎖。
- Read Uncommitted,讀寫均不使用鎖,數(shù)據(jù)的一致性最差,也會出現(xiàn)許多邏輯錯誤。
- Read Committed,使用寫鎖,但是讀會出現(xiàn)不一致,不可重復(fù)讀。
- Repeatable Read, 使用讀鎖和寫鎖,解決不可重復(fù)讀的問題,但會有幻讀。
- Serializable, 使用事務(wù)串形化調(diào)度,避免出現(xiàn)因為插入數(shù)據(jù)沒法加鎖導(dǎo)致的不一致的情況。
讀未提交,造成臟讀(Read Uncommitted)
一個事務(wù)中的讀操作可能讀到另一個事務(wù)中未提交修改的數(shù)據(jù),如果事務(wù)發(fā)生回滾就可能造成錯誤。
例子:A打100塊給B,B看賬戶,這是兩個操作,針對同一個數(shù)據(jù)庫,兩個事物,如果B讀到了A事務(wù)中的100塊,認(rèn)為錢打過來了,但是A的事務(wù)最后回滾了,造成損失。
避免這些事情的發(fā)生就需要我們在寫操作的時候加鎖,使讀寫分離,保證讀數(shù)據(jù)的時候,數(shù)據(jù)不被修改,寫數(shù)據(jù)的時候,數(shù)據(jù)不被讀取。從而保證寫的同時不能被另個事務(wù)寫和讀。
讀已提交(Read Committed)
我們加了寫鎖,就可以保證不出現(xiàn)臟讀,也就是保證讀的都是提交之后的數(shù)據(jù),但是會造成不可重讀,即讀的時候不加鎖,一個讀的事務(wù)過程中,如果讀取數(shù)據(jù)兩次,在兩次之間有寫事務(wù)修改了數(shù)據(jù),將會導(dǎo)致兩次讀取的結(jié)果不一致,從而導(dǎo)致邏輯錯誤。
可重復(fù)讀(Repeatable Read)
解決不可重復(fù)讀問題,一個事務(wù)中如果有多次讀取操作,讀取結(jié)果需要一致(指的是固定一條數(shù)據(jù)的一致,幻讀指的是查詢出的數(shù)量不一致,即不可重復(fù)讀對應(yīng)的是update語句,但是解決不掉insert語句導(dǎo)致的幻讀問題?。?/p>
所以讀鎖在事務(wù)中持有可以保證不出現(xiàn)不可重復(fù)讀,寫的時候必須加鎖且持有,這是必須的了,不然就會出現(xiàn)臟讀。Repeatable Read(可重讀)也是MySql的默認(rèn)事務(wù)隔離級別
串行化(Serializable)
解決幻讀問題,在同一個事務(wù)中,同一個查詢多次返回的結(jié)果不一致。事務(wù)A新增了一條記錄,事務(wù)B在事務(wù)A提交前后各執(zhí)行了一次查詢操作,發(fā)現(xiàn)后一次比前一次多了一條記錄。幻讀是由于并發(fā)事務(wù)增加記錄導(dǎo)致的,這個不能像不可重復(fù)讀通過記錄加鎖解決,因為對于新增的記錄根本無法加鎖。需要將事務(wù)串行化,才能避免幻讀。
這是最高的隔離級別,它通過強制事務(wù)排序,使之不可能相互沖突,從而解決幻讀問題。簡言之,它是在每個讀的數(shù)據(jù)行上加上共享鎖。在這個級別,可能導(dǎo)致大量的超時現(xiàn)象和鎖競爭
5.元數(shù)據(jù)鎖(MDL鎖)
MySQL5.5引入了meta data lock,簡稱MDL鎖,屬于表鎖范疇。MDL的作用是,保證讀寫的正確性。比如,如果一個查詢正在遍歷一個表中的數(shù)據(jù),而執(zhí)行期間另一個線程對這個表結(jié)構(gòu)做變更,增加了一列,那么查詢線程拿到的結(jié)果跟表結(jié)構(gòu)對不上,肯定是不行的。
因此,當(dāng)對一個表做增刪改查操作的時候,加MDL讀鎖;當(dāng)要對表做結(jié)構(gòu)變更操作的時候,加MDL寫鎖
例子:開啟一個事務(wù),進行查詢表操作,暫不提交事務(wù):(默認(rèn)加了一個MDL讀鎖)
之后在新事務(wù)中嘗試修改表結(jié)構(gòu),進入阻塞狀態(tài):(無法再添加DML寫鎖,存在互斥!)
6.間隙鎖
行鎖只能鎖住行,不能完全解決幻讀問題,新插入記錄這個動作,要更新的是記錄之間的“間隙”。因此,為了解決幻讀問題,InnoDB只好引入新的鎖,也就是間隙鎖
RR隔離級別下為了解決“幻讀”問題:“快照讀”依靠MVCC控制,“當(dāng)前讀”通過間隙鎖解決??
接下來我們用一個案例來解釋一下間隙鎖:
圖中id值為8的記錄加了gap鎖,意味著不允許別的事務(wù)在id值為8的記錄前邊的間隙插入新記錄,其實就是id列的值(3,8)
這個區(qū)間的新記錄是不允許立即插入的。比如,有另外一個事務(wù)再想插入一條id值為4的新記錄,它定位到該條新記錄的下一條記錄的id值為8,而這條記錄上又有一個gap鎖,所以就會阻塞插入操作,直到擁有這個gap鎖的事務(wù)提交了之后,id列的值在區(qū)間(3,8)
中的新記錄才可以被插入。
gap鎖的提出僅僅是為了防止插入幻影記錄而提出的。雖然有共享gap鎖和獨占gap鎖這樣的說法,但是它們起到的作用是相同的。而且如果對一條記錄加了gap鎖(不論是共享gap鎖還是獨占gap鎖),并不會限制其他事務(wù)對這條記錄加記錄鎖或者繼續(xù)加gap鎖。
間隙鎖與間隙鎖之間是不存在沖突的(可以共存),沖突的是往間隙里插入一條記錄?。ú辉S插入)??
7.臨鍵鎖
有時候我們既想鎖住某條記錄,又想阻止其他事務(wù)在該記錄前邊的間隙插入新記錄,所以InnoDB就提出了一種稱之為 Next-Key Locks
的鎖,官方的類型名稱為:LOCK_ORDINARY
,我們也可以簡稱為next-key
鎖。Next-Key Locks
是在存儲引擎innodb、事務(wù)級別在可重復(fù)讀的情況下使用的數(shù)據(jù)庫鎖,innodb默認(rèn)的鎖就是Next-Key locks
。比如,我們把id值為8的那條記錄加一個next-key
鎖的示意圖如下:
next-key
鎖的本質(zhì)就是一個記錄鎖和一個gap鎖的合體,它既能保護該條記錄,又能阻止別的事務(wù)將新記錄插入被保護記錄前邊的間隙??
8.插入意向鎖
我們說一個事務(wù)在插入一條記錄時需要判斷一下插入位置是不是被別的事務(wù)加了gap鎖(next-key
鎖也包含gap鎖),如果有的話,插入操作需要等待,直到擁有g(shù)ap鎖的那個事務(wù)提交。但是InnoDB規(guī)定事務(wù)在等待的時候也需要在內(nèi)存中生成一個鎖結(jié)構(gòu),表明有事務(wù)想在某個間隙中插入新記錄,但是現(xiàn)在在等待。InnoDB就把這種類型的鎖命名為Insert Intention Locks
,官方的類型名稱為:LOCK_INSERT_INTENTION
,我們稱為插入意向鎖。插入意向鎖是一種Gap鎖,不是意向鎖,在insert操作時產(chǎn)生。
插入意向鎖是在插入一條記錄行前,由INSERT操作產(chǎn)生的一種間隙鎖。該鎖用以表示插入意向,當(dāng)多個事務(wù)在同一區(qū)間(gap)插入位置不同的多條數(shù)據(jù)時,事務(wù)之間不需要互相等待。假設(shè)存在兩條值分別為4和7的記錄,兩個不同的事務(wù)分別試圖插入值為5和6的兩條記錄,每個事務(wù)在獲取插入行上獨占的(排他)鎖前,都會獲取(4,7)
之間的間隙鎖,但是因為數(shù)據(jù)行之間并不沖突,所以兩個事務(wù)之間并不會產(chǎn)生沖突(阻塞等待)??偨Y(jié)來說,插入意向鎖的特性可以分成兩部分:文章來源:http://www.zghlxwxcb.cn/news/detail-449500.html
- 插入意向鎖是一種特殊的間隙鎖 - 間隙鎖可以鎖定開區(qū)間內(nèi)的部分記錄。
- 插入意向鎖之間互不排斥,所以即使多個事務(wù)在同一區(qū)間插入多條記錄,只要記錄本身(主鍵、唯一索引)不沖突,那么事務(wù)之間就不會出現(xiàn)沖突等待。
本教程部分基于CSDN博主:Stephen.W
文章來源地址http://www.zghlxwxcb.cn/news/detail-449500.html
到了這里,關(guān)于詳解數(shù)據(jù)庫的鎖機制及原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!