原文鏈接:blog.ouyangsihai.cn >> MySQL的又一神器-鎖,MySQL面試必備
在山陽等地區(qū),都構建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務理念,為客戶提供成都做網(wǎng)站、成都網(wǎng)站制作 網(wǎng)站設計制作按需定制網(wǎng)站,公司網(wǎng)站建設,企業(yè)網(wǎng)站建設,高端網(wǎng)站設計,網(wǎng)絡營銷推廣,外貿(mào)網(wǎng)站建設,山陽網(wǎng)站建設費用合理。
在生活中鎖的例子多的不能再多了,從古老的簡單的門鎖,到密碼鎖,再到現(xiàn)在的指紋解鎖,人臉識別鎖,這都是鎖的鮮明的例子,所以,我們理解鎖應該是非常簡單的。
再到MySQL中的鎖,對于MySQL來說,鎖是一個很重要的特性,數(shù)據(jù)庫的鎖是為了支持對共享資源進行并發(fā)訪問,提供數(shù)據(jù)的完整性和一致性,這樣才能保證在高并發(fā)的情況下,訪問數(shù)據(jù)庫的時候,數(shù)據(jù)不會出現(xiàn)問題。
在數(shù)據(jù)庫中,lock和latch都可以稱為鎖,但是意義卻不同。
Latch一般稱為閂鎖
(輕量級的鎖),因為其要求鎖定的時間必須非常短。若持續(xù)的時間長,則應用的性能會非常差,在InnoDB引擎中,Latch又可以分為mutex
(互斥量)和rwlock
(讀寫鎖)。其目的是用來保證并發(fā)線程操作臨界資源的正確性,并且通常沒有死鎖檢測的機制。
Lock的對象是事務
,用來鎖定的是數(shù)據(jù)庫中的對象,如表、頁、行。并且一般lock的對象僅在事務commit或rollback后進行釋放(不同事務隔離級別釋放的時間可能不同)。
在數(shù)據(jù)庫中,鎖的粒度的不同可以分為表鎖、頁鎖、行鎖,這些鎖的粒度之間也是會發(fā)生升級的,鎖升級的意思就是講當前鎖的粒度降低,數(shù)據(jù)庫可以把一個表的1000個行鎖升級為一個頁鎖,或者將頁鎖升級為表鎖,下面分別介紹一下這三種鎖的粒度(參考自博客:https://blog.csdn.net/baolingye/article/details/102506072)。
表級別的鎖定是MySQL各存儲引擎中最大顆粒度的鎖定機制。該鎖定機制最大的特點是實現(xiàn)邏輯非常簡單,帶來的系統(tǒng)負面影響最小。所以獲取鎖和釋放鎖的速度很快。由于表級鎖一次會將整個表鎖定,所以可以很好的避免困擾我們的死鎖問題。
當然,鎖定顆粒度大所帶來最大的負面影響就是出現(xiàn)鎖定資源爭用的概率也會最高,致使并大度大打折扣。
使用表級鎖定的主要是MyISAM,MEMORY,CSV等一些非事務性存儲引擎。
特點:開銷小,加鎖快;不會出現(xiàn)死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低。
頁級鎖定是MySQL中比較獨特的一種鎖定級別,在其他數(shù)據(jù)庫管理軟件中也并不是太常見。頁級鎖定的特點是鎖定顆粒度介于行級鎖定與表級鎖之間,所以獲取鎖定所需要的資源開銷,以及所能提供的并發(fā)處理能力也同樣是介于上面二者之間。另外,頁級鎖定和行級鎖定一樣,會發(fā)生死鎖。
在數(shù)據(jù)庫實現(xiàn)資源鎖定的過程中,隨著鎖定資源顆粒度的減小,鎖定相同數(shù)據(jù)量的數(shù)據(jù)所需要消耗的內(nèi)存數(shù)量是越來越多的,實現(xiàn)算法也會越來越復雜。不過,隨著鎖定資源 顆粒度的減小,應用程序的訪問請求遇到鎖等待的可能性也會隨之降低,系統(tǒng)整體并發(fā)度也隨之提升。
使用頁級鎖定的主要是BerkeleyDB存儲引擎。
特點:開銷和加鎖時間界于表鎖和行鎖之間;會出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般。
行級鎖定最大的特點就是鎖定對象的粒度很小,也是目前各大數(shù)據(jù)庫管理軟件所實現(xiàn)的鎖定顆粒度最小的。由于鎖定顆粒度很小,所以發(fā)生鎖定資源爭用的概率也最小,能夠給予應用程序盡可能大的并發(fā)處理能力而提高一些需要高并發(fā)應用系統(tǒng)的整體性能。
雖然能夠在并發(fā)處理能力上面有較大的優(yōu)勢,但是行級鎖定也因此帶來了不少弊端。由于鎖定資源的顆粒度很小,所以每次獲取鎖和釋放鎖需要做的事情也更多,帶來的消耗自然也就更大了。此外,行級鎖定也最容易發(fā)生死鎖。
特點:開銷大,加鎖慢;會出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。
比較表鎖我們可以發(fā)現(xiàn),這兩種鎖的特點基本都是相反的,而從鎖的角度來說,表級鎖更適合于以查詢?yōu)橹鳎挥猩倭堪此饕龡l件更新數(shù)據(jù)的應用,如Web應用;而行級鎖則更適合于有大量按索引條件并發(fā)更新少量不同數(shù)據(jù),同時又有并發(fā)查詢的應用,如一些在線事務處理(OLTP)系統(tǒng)。
InnoDB存儲引擎中存在著不同類型的鎖,下面一一介紹一下。
S or X (共享鎖、排他鎖)
數(shù)據(jù)的操作其實只有兩種,也就是讀和寫,而數(shù)據(jù)庫在實現(xiàn)鎖時,也會對這兩種操作使用不同的鎖;InnoDB 實現(xiàn)了標準的行級鎖,也就是共享鎖(Shared Lock)和互斥鎖(Exclusive Lock)。
IS or IX (共享、排他)意向鎖
為了允許行鎖和表鎖共存,實現(xiàn)多粒度鎖機制,InnoDB存儲引擎支持一種額外的鎖方式,就稱為意向鎖,意向鎖在 InnoDB 中是表級鎖,意向鎖分為:
另外,這些鎖之間的并不是一定可以共存的,有些鎖之間是不兼容的,所謂兼容性就是指事務 A 獲得一個某行某種鎖之后,事務 B 同樣的在這個行上嘗試獲取某種鎖,如果能立即獲取,則稱鎖兼容,反之叫沖突。
下面我們再看一下這兩種鎖的兼容性。
這里用一個思維導圖把前面的概念做一個小結。
在一個事務中查詢數(shù)據(jù)時,普通的SELECT語句不會對查詢的數(shù)據(jù)進行加鎖,其他事務仍可以對查詢的數(shù)據(jù)執(zhí)行更新和刪除操作。因此,InnoDB提供了兩種類型的鎖定讀來保證額外的安全性:
SELECT ... LOCK IN SHARE MODE
SELECT ... FOR UPDATE
SELECT ... LOCK IN SHARE MODE
: 對讀取的行添加S鎖,其他事物可以對這些行添加S鎖,若添加X鎖,則會被阻塞。
SELECT ... FOR UPDATE
: 會對查詢的行及相關聯(lián)的索引記錄加X鎖,其他事務請求的S鎖或X鎖都會被阻塞。 當事務提交或回滾后,通過這兩個語句添加的鎖都會被釋放。 注意:只有在自動提交被禁用時,SELECT FOR UPDATE才可以鎖定行,若開啟自動提交,則匹配的行不會被鎖定。
一致性非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎通過多版本控制(MVVC)讀取當前數(shù)據(jù)庫中行數(shù)據(jù)的方式。如果讀取的行正在執(zhí)行DELETE或UPDATE操作,這時讀取操作不會因此去等待行上鎖的釋放。相反地,InnoDB會去讀取行的一個快照。所以,非鎖定讀機制大大提高了數(shù)據(jù)庫的并發(fā)性。
一致性非鎖定讀是InnoDB默認的讀取方式,即讀取不會占用和等待行上的鎖。在事務隔離級別READ COMMITTED
和REPEATABLE READ
下,InnoDB使用一致性非鎖定讀。
然而,對于快照數(shù)據(jù)的定義卻不同。在READ COMMITTED
事務隔離級別下,一致性非鎖定讀總是讀取被鎖定行的最新一份快照數(shù)據(jù)。而在REPEATABLE READ
事務隔離級別下,則讀取事務開始時的行數(shù)據(jù)版本。
下面我們通過一個簡單的例子來說明一下這兩種方式的區(qū)別。
首先創(chuàng)建一張表;
插入一條數(shù)據(jù);
insert into lock_test values(1);
查看隔離級別;
select @@tx_isolation;
下面分為兩種事務進行操作。
在REPEATABLE READ
事務隔離級別下;
在REPEATABLE READ
事務隔離級別下,讀取事務開始時的行數(shù)據(jù),所以當會話B修改了數(shù)據(jù)之后,通過以前的查詢,還是可以查詢到數(shù)據(jù)的。
在READ COMMITTED
事務隔離級別下;
在READ COMMITTED
事務隔離級別下,讀取該行版本最新的一個快照數(shù)據(jù),所以,由于B會話修改了數(shù)據(jù),并且提交了事務,所以,A讀取不到數(shù)據(jù)了。
InnoDB存儲引擎有3種行鎖的算法,其分別是:
Record Lock:總是會去鎖住索引記錄,如果InnoDB存儲引擎表在建立的時候沒有設置任何一個索引,那么這時InnoDB存儲引擎會使用隱式的主鍵來進行鎖定。
Next-Key Lock:結合了Gap Lock和Record Lock的一種鎖定算法,在Next-Key Lock算法下,InnoDB對于行的查詢都是采用這種鎖定算法。舉個例子10,20,30,那么該索引可能被Next-Key Locking的區(qū)間為:
除了Next-Key Locking,還有Previous-Key Locking技術,這種技術跟Next-Key Lock正好相反,鎖定的區(qū)間是區(qū)間范圍和前一個值。同樣上述的值,使用Previous-Key Locking技術,那么可鎖定的區(qū)間為:
不是所有索引都會加上Next-key Lock的,這里有一種特殊的情況,在查詢的列是唯一索引(包含主鍵索引)的情況下,Next-key Lock
會降級為Record Lock
。
接下來,我們來通過一個例子解釋一下。
CREATE TABLE test (
x INT,
y INT,
PRIMARY KEY(x), // x是主鍵索引
KEY(y) // y是普通索引
);
INSERT INTO test select 3, 2;
INSERT INTO test select 5, 3;
INSERT INTO test select 7, 6;
INSERT INTO test select 10, 8;
我們現(xiàn)在會話A中執(zhí)行如下語句;
SELECT * FROM test WHERE y = 3 FOR UPDATE
我們分析一下這時候的加鎖情況。
用戶可以通過以下兩種方式來顯示的關閉Gap Lock:
Gap Lock的作用:是為了阻止多個事務將記錄插入到同一個范圍內(nèi),設計它的目的是用來解決Phontom Problem(幻讀問題)。在MySQL默認的隔離級別(Repeatable Read)下,InnoDB就是使用它來解決幻讀問題。
幻讀:是指在同一事務下,連續(xù)執(zhí)行兩次同樣的SQL語句可能導致不同的結果,第二次的SQL可能會返回之前不存在的行,也就是第一次執(zhí)行和第二次執(zhí)行期間有其他事務往里插入了新的行。
臟讀:在不同的事務下,當前事務可以讀到另外事務未提交的數(shù)據(jù)。另外我們需要注意的是默認的MySQL隔離級別是REPEATABLE READ
是不會發(fā)生臟讀的,臟讀發(fā)生的條件是需要事務的隔離級別為READ UNCOMMITTED
,所以如果出現(xiàn)臟讀,可能就是這種隔離級別導致的。
下面我們通過一個例子看一下。
從上面這個例子可以看出,當我們的事務的隔離級別為READ UNCOMMITTED
的時候,在會話A還沒有提交時,會話B就能夠查詢到會話A沒有提交的數(shù)據(jù)。
不可重復讀:是指在一個事務內(nèi)多次讀取同一集合的數(shù)據(jù),但是多次讀到的數(shù)據(jù)是不一樣的,這就違反了數(shù)據(jù)庫事務的一致性的原則。但是,這跟臟讀還是有區(qū)別的,臟讀的數(shù)據(jù)是沒有提交的,但是不可重復讀的數(shù)據(jù)是已經(jīng)提交的數(shù)據(jù)。
我們通過下面的例子來看一下這種問題的發(fā)生。
從上面的例子可以看出,在A的一次會話中,由于會話B插入了數(shù)據(jù),導致兩次查詢的結果不一致,所以就出現(xiàn)了不可重復讀的問題。
我們需要注意的是不可重復讀讀取的數(shù)據(jù)是已經(jīng)提交的數(shù)據(jù),事務的隔離級別為READ COMMITTED
,這種問題我們是可以接受的。
如果我們需要避免不可重復讀的問題的發(fā)生,那么我們可以使用Next-Key Lock算法(設置事務的隔離級別為READ REPEATABLE
)來避免,在MySQL中,不可重復讀問題就是Phantom Problem,也就是幻像問題。
丟失更新:指的是一個事務的更新操作會被另外一個事務的更新操作所覆蓋,從而導致數(shù)據(jù)的不一致。在當前數(shù)據(jù)庫的任何隔離級別下都不會導致丟失更新問題,要出現(xiàn)這個問題,在多用戶計算機系統(tǒng)環(huán)境下有可能出現(xiàn)這種問題。
如何避免丟失更新的問題呢,我們只需要讓事務的操作變成串行化,不要并行執(zhí)行就可以。
我們一般使用SELECT ... FOR UPDATE
語句,給操作加上一個排他X鎖。
這里我們做一個小結,主要是在不同的事務的隔離級別下出現(xiàn)的問題的對照,這樣就更加清晰了。
文章有不當之處,歡迎指正,如果喜歡微信閱讀,你也可×××學java`,獲取優(yōu)質學習資源。