概述
MySQL中的鎖比較繁雜,但其實MySQL中的鎖本質上并沒有那么多,只是我們站在不同維度上對鎖進行的分類,本文主要介紹MySQL中鎖的類型及用途。
鎖的分類
基于鎖的屬性分類,可以分為:
- 共享鎖
- 排他鎖
基于鎖的粒度分類,可以分為:
- 行級鎖(InnoDB)
- 表級鎖(InnoDB、MyISAM)
- 頁級鎖(BDB引擎)
- 記錄鎖
- 間隙鎖
- 臨鍵鎖
基于鎖的狀態分類:可以分為:
- 意向共享鎖
- 意向排他鎖
鎖介紹
共享鎖(Share Lock)
共享鎖又稱讀鎖,簡稱S鎖。
當一個事務為數據加上讀鎖后,其他事務只能對該數據加讀鎖而不能對數據加寫鎖,直到所有的讀鎖釋放之后其他事務才能對其加寫鎖。
共享鎖的特性主要是為了支持并發的讀取數據,讀取數據的時候不支持對數據進行任何修改。
排他鎖(Exclusive Lock)
排他鎖又稱寫鎖,簡稱X鎖。
當一個事務為數據加上寫鎖后,其他事務將不能再為數據加任何鎖,直到該鎖釋放之后其他事務才能對數據進行加鎖。
排他鎖的目的是在數據修改的時候,不允許其他人同時修改,也不允許其他人讀取,避免出現臟數據和臟讀的問題。
表鎖(Table Lock)
表鎖是指上鎖的時候鎖住的是整個表,當下一個事務訪問該表的時候,必須等前一個事務釋放了鎖,才能進行對表的訪問。
其特點是:粒度大,加鎖簡單,容易沖突,所以支持的并發較低。
行鎖(Row Lock)
行鎖是指上鎖的時候鎖住的是表中的某一行或多行記錄,其他事務不能訪問被鎖住的記錄,但可以訪問未被鎖住的記錄。
其特點是:粒度小,加鎖比表鎖麻煩,但不容易沖突,相比表鎖可以支持更高的并發。
記錄鎖(Record Lock)
記錄鎖屬于行鎖的一種,只不過記錄鎖的范圍只是表中的某一條記錄
。
那么什么時候會出現記錄鎖呢?
答案是精確條件命中,并且命中的條件字段是唯一索引。
如果你使用的是普通索引,雖然某個查詢最終也僅命中了表中的1條記錄,它也不會加記錄鎖,必須是命中了唯一索引
加了記錄鎖之后,可以避免數據在查詢的時候被修改,也避免了在修改的事務未提交前被其他事務讀取,引發臟讀問題。
頁鎖(Page Lock)
頁級鎖是MySQL中鎖定粒度介于行級鎖和表級鎖中間的一種鎖。
表級鎖加鎖速度快,但沖突多,行級鎖沖突少,但是加鎖速度慢,而頁級鎖相對折中,一次鎖定一頁的數據。
其特點是:鎖定粒度、開銷和加鎖時間介于表鎖和行鎖之間,并發度一般,和行鎖一樣也會出現死鎖。
間隙鎖(Gap Lock)
屬于行鎖的一種,間隙鎖是在事務加鎖后其鎖住的是表記錄的某一個區間,包括記錄之間的間隙,遵循左開右閉原則。
它的存在可以解決幻讀問題,另外需要注意,Gap Lock 只在 REPEATABLE READ 隔離級別下有效(在MySQL的 REPEATABLE READ 隔離級別中,它已經幫我們解決了幻讀問題,解決的方案就是 Gap Lock)
臨鍵鎖(Next-Key Lock)
臨鍵鎖也屬于行鎖的一種,并且它是InnoDB引擎的行鎖默認算法。
臨鍵鎖Next-Key Lock 可以理解為是 Record Lock 和 Gap Lock 的結合體。
正常來說,我們加行鎖的基本單位就是 Next-Key Lock,即既有記錄鎖又有間隙鎖(但是有時候 Next-Key Lock 會退化)。
Next-Key Lock 的加鎖規則:
- 鎖的范圍是左開右閉。
- 如果是唯一非空索引的等值查詢,Next-Key Lock 會退化成 Record Lock。
- 普通索引上的等值查詢,向后遍歷時,最后一個不滿足等值條件的時候,Next-Key Lock 會退化成 Gap Lock。
結合記錄鎖和間隙鎖的特性,臨鍵鎖避免了出現臟讀、重復度、幻讀的問題,加了臨鍵鎖之后,在范圍區間內的數據不允許被修改和插入。
意向鎖(Intention Lock)
InnoDB支持多粒度鎖,為了允許行鎖和表鎖共存,InnoDB引入了意向鎖的概念。
意向鎖是一種表級鎖,它用來指示事務稍后對表中的行需要哪種類型的鎖(共享或獨占)
意向鎖分為:
- 意向共享鎖(簡稱IS):事務想要獲取到一張表中某幾行的共享鎖
事務想要在獲得表中某些記錄的共享鎖,需要在表上先加意向共享鎖。 - 意向排他鎖(簡稱IX):事務想要獲取到一張表中某幾行的排他鎖
事務想要在獲得表中某些記錄的互斥鎖,需要在表上先加意向互斥鎖。
值得注意的是:
- 意向共享鎖和意向排它鎖之間不會發生沖突
- 意向鎖也不會和
數據行
共享鎖S、排它鎖X發生沖突
下面的矩陣總結了表級鎖
類型的兼容性:
如果鎖與現有鎖兼容,則將鎖授予請求事務,但如果與現有鎖沖突則不會,事務會等待直到釋放沖突的現有鎖。
自增鎖(AutoInc Lock)
AUTOINC 鎖又叫自增鎖(一般簡寫成 AI 鎖)。
它是一種表鎖,當表中有自增列(AUTOINCREMENT)時出現。
當插入表中有自增列時,數據庫需要自動生成自增值,它會先為該表加 AUTOINC 表鎖,阻塞其他事務的插入操作,這樣保證生成的自增值肯定是唯一的。
AUTOINC 鎖具有如下特點:
- AUTO_INC 鎖互不兼容,也就是說同一張表同時只允許有一個自增鎖;
- 自增值一旦分配了就會 +1,如果事務回滾,自增值也不會減回去,所以自增值可能會出現中斷的情況。
顯然,AUTOINC 表鎖會導致并發插入的效率降低,為了提高插入的并發性,MySQL 從 5.1.22 版本開始,引入了一種可選的輕量級鎖(mutex)機制來代替 AUTOINC 鎖,可以通過參數 innodbautoinclockmode 來靈活控制分配自增值時的并發策略。具體可以參考 MySQL 的 AUTOINCREMENT Handling in InnoDB 一文。