你需要理解的關(guān)于MySQL的鎖知識
一、前言
MySQL 的鎖按照范圍可以分為全局鎖、表鎖、行鎖,其中行鎖是由數(shù)據(jù)庫引擎實(shí)現(xiàn)的,并不是所有的引擎都提供行鎖,MyISAM 就不支持行鎖,所以文章介紹行鎖會以InnoDB引擎為例來介紹行鎖。
二、全局鎖
MySQL 提供全局鎖來對整個數(shù)據(jù)庫實(shí)例加鎖。
語法:
FLUSH TABLES WITH READ LOCK
這條語句一般都是用來備份的,當(dāng)執(zhí)行這條語句后,數(shù)據(jù)庫所有打開的表都會被關(guān)閉,并且使用全局讀鎖鎖定數(shù)據(jù)庫的所有表,同時(shí),其他線程的更新語句(增刪改),數(shù)據(jù)定義語句(建表,修改表結(jié)構(gòu))和更新類的事務(wù)提交都會被阻塞。
在mysql 8.0 以后,對于備份,mysql可以直接使用備份鎖。
語句:
LOCK INSTANCE FOR BACKUPUNLOCK INSTANCE
這個鎖的作用范圍更廣,這個鎖會阻止文件的創(chuàng)建,重命名,刪除,包括 REPAIR TABLE TRUNCATE TABLE, OPTIMIZE TABLE
操作以及賬戶的管理都會被阻塞。當(dāng)然這些操作對于內(nèi)存臨時(shí)表來說是可以執(zhí)行的,為什么內(nèi)存表不受這些限制呢?因?yàn)閮?nèi)存表不需要備份,所以也就沒必要滿足這些條件。
三、表鎖
Mysql的表級別鎖分為兩類,一類是元數(shù)據(jù)鎖(Metadata Lock,MDL),一種是表鎖。
元數(shù)據(jù)鎖(MDL) 不需要顯式使用,在訪問一個表的時(shí)候會被自動加上。這個特性需要MySQL5.5版本以上才會支持,當(dāng)對一個表做增刪改查的時(shí)候,該表會被加MDL讀鎖;當(dāng)對表做結(jié)構(gòu)變更的時(shí)候,加MDL寫鎖。MDL鎖有一些規(guī)則:
讀鎖之間不互斥,所以可以多線程多同一張表進(jìn)行增刪改查。讀寫鎖、寫鎖之間是互斥的,為了保證表結(jié)構(gòu)變更的安全性,所以如果要多線程對同一個表加字段等表結(jié)構(gòu)操作,就會變成串行化,需要進(jìn)行鎖等待。MDL的寫鎖優(yōu)先級比MDL讀鎖的優(yōu)先級,但是可以設(shè)置max_write_lock_count系統(tǒng)變量來改變這種情況,當(dāng)寫鎖請求超過這個變量設(shè)置的數(shù)后,MDL讀鎖的優(yōu)先級會比MDL寫鎖的優(yōu)先級高。(默認(rèn)情況下,這個數(shù)字會很大,所以不用擔(dān)心寫鎖的優(yōu)先級下降)MDL的鎖釋放必須要等到事務(wù)結(jié)束才會釋放。
所以我們在操作數(shù)據(jù)庫表結(jié)構(gòu)時(shí)候必須要注意不要使用長事務(wù),這里具體是什么意思呢?我舉個例子說明下:
上圖表示演示了4個session執(zhí)行語句,首先SessionA開啟了事務(wù)沒有提交,接著sessionB執(zhí)行查詢,因?yàn)槭谦@取MDL讀鎖,所以互相不影響,可以正常執(zhí)行,SessionC新增一個字段,由于MDL寫和讀是互斥的,所以SessionC會被阻塞,之后SessionD開始執(zhí)行一個查詢語句,由于SessionC的阻塞,所以SessionD也阻塞了。所以,我們模擬的SessionA的事務(wù)是長事務(wù),然后后面執(zhí)行了修改表結(jié)構(gòu),會導(dǎo)致后續(xù)對該表所有的讀寫操作都不可行了。所以在實(shí)際場景中,如果業(yè)務(wù)請求比較頻繁的時(shí)候,對表結(jié)構(gòu)進(jìn)行修改的時(shí)候就有可能導(dǎo)致該庫的線程被阻塞滿。
表鎖 的語法如下:
LOCK TABLES tbl_name [[AS] alias] lock_type [, tbl_name [[AS] alias] lock_type] ...lock_type: { READ [LOCAL] | [LOW_PRIORITY] WRITE}UNLOCK TABLES
表鎖分為讀鎖和寫鎖,讀鎖不互斥,但是獲取讀鎖不能寫入數(shù)據(jù),其他沒有獲取到讀鎖的session也是可以讀取表的,所以讀鎖的目的就是限制表被寫。如果表被讀鎖鎖住后,再執(zhí)行插入語句會報(bào)錯,報(bào)錯如下:
1099 - Table 'XXXX' was locked with a READ lock and can't be updated
寫鎖被獲取后可以對表進(jìn)行讀寫,寫鎖是互斥的,一旦某個session獲取到表的寫鎖,另外的session無法訪問這個表,直到寫鎖被釋放。
表的解鎖可以使用unlock tables
解鎖,也可以客戶端口自動解鎖。lock tables
鎖表會獨(dú)占式的鎖住表,除了限制其他線程對該表的讀寫,也會限制本線程接下來的操作對象。
四、行鎖(InnoDB)
MySQL的行鎖是在引擎層面實(shí)現(xiàn)的,所以這里討論的也是InnoDB引擎下的行鎖,下面會詳細(xì)介紹InnoDB下常見的幾種行鎖
4.1 共享鎖
共享鎖能允許事務(wù)獲取到鎖后進(jìn)行讀操作,共享鎖是不互斥的,一個事務(wù)獲取到共享鎖后,另外一個事務(wù)也可以獲取共享鎖,獲取共享鎖后不能進(jìn)行寫操作。
4.2 排它鎖
排他鎖允許事務(wù)獲取到鎖后進(jìn)行更新一行或者刪除某一行操作,排他鎖顧名思義是互斥的,一個事務(wù)獲取到排他鎖后,其他事務(wù)不能獲取到排他鎖,直到這個鎖被釋放。
4.3 意向鎖
InnoDB支持多種粒度的鎖,允許行鎖和表鎖共存,這里說的意向鎖其實(shí)是一種表級別的鎖,但是我把它放在行鎖里面是因?yàn)樗粫为?dú)存在,它的出現(xiàn)肯定會伴隨著行鎖(共享鎖或者排他鎖),它主要的目的就是表示將要鎖定表中的行或者正在鎖定表中的行。
意向鎖根據(jù)和行鎖的組合可以分為:
- 意向排他鎖:表明將要在表中的某些行獲取排他鎖
- 意向共享鎖:表明將要在表中的某些行獲取共享鎖
意向鎖的獲取必須在行鎖獲取之前,也就是說獲取共享鎖之前必須先要獲取共享意向鎖,對于排他鎖也是一樣的道理。
那么這個意向鎖到底有什么作用呢?
解釋這個之前,我們先看看意向鎖和行鎖之前的兼容關(guān)系:
--- | 排他鎖(X) | 意向排他鎖(IX) | 共享鎖(S) | 意向共享鎖(IS) |
---|---|---|---|---|
排他鎖(X) | 沖突 | 沖突 | 沖突 | 沖突 |
意向排他鎖(IX) | 沖突 | 兼容 | 沖突 | 兼容 |
共享鎖(S) | 沖突 | 沖突 | 兼容 | 兼容 |
意向共享鎖(IS) | 沖突 | 兼容 | 兼容 | 兼容 |
我們假設(shè)有2個事務(wù)A和事務(wù)B,事務(wù)獲取到了共享鎖,鎖住了表中的某一行,這一行只能讀,不能寫,現(xiàn)在事務(wù)B要申請整個表的寫鎖。如果事務(wù)B申請成功,那么肯定是可以對表中所有的行進(jìn)行寫操作的,那么肯定與A獲取的行鎖沖突。數(shù)據(jù)庫為了避免這種沖突,就會進(jìn)行沖突檢測,那么如何去檢測呢?有兩種方式:
判斷表是否已經(jīng)被其他事務(wù)用表級鎖鎖住。判斷表中的每一行是否被行鎖鎖住。
判斷表中的每一行需要遍歷所有記錄,效率太差,所以數(shù)據(jù)庫就用第一種方式去做沖突檢測,也就是用到了意向鎖。
總結(jié)
本文主要從MySQL的加鎖范圍來分析了MySQL的鎖,MySQL根據(jù)加鎖范圍可以分為全局鎖、表鎖、行鎖。全局鎖和表鎖是MySQL自己實(shí)現(xiàn),行鎖都是由引擎層面去實(shí)現(xiàn)。InnoDB下的行鎖主要分為共享鎖和排他鎖。共享鎖請求后,行只能讀,共享鎖之間不互斥。排他鎖獲取后能更新和刪除行,排他鎖與其他鎖都互斥。最后我在行鎖的基礎(chǔ)上提到了意向鎖,意向鎖主要表示正在鎖住行或者即將鎖住行,為了在鎖沖突檢測中提高效率。當(dāng)然InnoDB下還有其他鎖,比如間隙鎖,記錄鎖,Next-Key鎖等,這些都不在本文的探討范圍之內(nèi),如有興趣的同學(xué)可以自行研究。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
設(shè)置MySQLroot賬戶密碼報(bào)錯ERROR 1064 (42000): You 
在安裝mysql的時(shí)候,設(shè)置root賬戶密碼出現(xiàn)了ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds..錯誤,本文小編給大家介紹了相關(guān)的解決方案,需要的朋友可以參考下2023-12-12Mysql8創(chuàng)建用戶及賦權(quán)操作實(shí)戰(zhàn)記錄
一般在開發(fā)中,我們需要新建一個賬戶,并賦予某個數(shù)據(jù)庫的訪問權(quán)限,下面這篇文章主要給大家介紹了關(guān)于Mysql8創(chuàng)建用戶及賦權(quán)操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04mysql安裝報(bào)錯unknown?variable?‘mysqlx_port=0.0‘簡單解決過程
這篇文章主要給大家介紹了關(guān)于mysql安裝報(bào)錯unknown?variable?‘mysqlx_port=0.0‘的解決過程,文中通過代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-08-08如何安裝MySQL Community Server 5.6.39
這篇文章主要為大家詳細(xì)介紹了MySQL Community Server 5.6.39安裝配置方法圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09MYSQL中有關(guān)SUM字段按條件統(tǒng)計(jì)使用IF函數(shù)(case)問題
MYSQL中SUM字段按條件統(tǒng)計(jì)使用IF函數(shù),具體實(shí)現(xiàn)代碼如下,感興趣的朋友不要錯過2014-01-01一次docker登錄mysql報(bào)錯問題的實(shí)戰(zhàn)記錄
這篇文章主要給大家介紹了一次docker登錄mysql報(bào)錯問題的實(shí)戰(zhàn)記錄,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用docker具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01