對於MySQL你必需要了解的鎖知識

1、前言

MySQL 的鎖按照範圍能夠分爲全局鎖、表鎖、行鎖,其中行鎖是由數據庫引擎實現的,並非全部的引擎都提供行鎖,MyISAM 就不支持行鎖,因此文章介紹行鎖會以InnoDB引擎爲例來介紹行鎖。html

2、全局鎖

MySQL 提供全局鎖來對整個數據庫實例加鎖。mysql

語法:sql

FLUSH TABLES WITH READ LOCK

這條語句通常都是用來備份的,當執行這條語句後,數據庫全部打開的表都會被關閉,而且使用全局讀鎖鎖定數據庫的全部表,同時,其餘線程的更新語句(增刪改),數據定義語句(建表,修改表結構)和更新類的事務提交都會被阻塞。數據庫

在mysql 8.0 之後,對於備份,mysql能夠直接使用備份鎖。安全

語句:session

LOCK INSTANCE FOR BACKUP

UNLOCK INSTANCE

這個鎖的做用範圍更廣,這個鎖會阻止文件的建立,重命名,刪除,包括 REPAIR TABLE TRUNCATE TABLE, OPTIMIZE TABLE操做以及帳戶的管理都會被阻塞。固然這些操做對於內存臨時表來講是能夠執行的,爲何內存表不受這些限制呢?由於內存表不須要備份,因此也就不必知足這些條件。多線程

3、表鎖

Mysql的表級別鎖分爲兩類,一類是元數據鎖(Metadata Lock,MDL),一種是表鎖。線程

元數據鎖(MDL) 不須要顯式使用,在訪問一個表的時候會被自動加上。這個特性須要MySQL5.5版本以上纔會支持,當對一個表作增刪改查的時候,該表會被加MDL讀鎖;當對錶作結構變動的時候,加MDL寫鎖。MDL鎖有一些規則:code

  • 讀鎖之間不互斥,因此能夠多線程多同一張表進行增刪改查。
  • 讀寫鎖、寫鎖之間是互斥的,爲了保證表結構變動的安全性,因此若是要多線程對同一個表加字段等表結構操做,就會變成串行化,須要進行鎖等待。
  • MDL的寫鎖優先級比MDL讀鎖的優先級,可是能夠設置max_write_lock_count系統變量來改變這種狀況,當寫鎖請求超過這個變量設置的數後,MDL讀鎖的優先級會比MDL寫鎖的優先級高。(默認狀況下,這個數字會很大,因此不用擔憂寫鎖的優先級降低)
  • MDL的鎖釋放必需要等到事務結束纔會釋放

因此咱們在操做數據庫表結構時候必需要注意不要使用長事務,這裏具體是什麼意思呢?我舉個例子說明下:htm

![](

上圖表示演示了4個session執行語句,首先SessionA開啓了事務沒有提交,接着sessionB執行查詢,由於是獲取MDL讀鎖,因此互相不影響,能夠正常執行,SessionC新增一個字段,因爲MDL寫和讀是互斥的,因此SessionC會被阻塞,以後SessionD開始執行一個查詢語句,因爲SessionC的阻塞,因此SessionD也阻塞了。因此,咱們模擬的SessionA的事務是長事務,而後後面執行了修改表結構,會致使後續對該表全部的讀寫操做都不可行了。因此在實際場景中,若是業務請求比較頻繁的時候,對錶結構進行修改的時候就有可能致使該庫的線程被阻塞滿。

表鎖 的語法以下:

LOCK TABLES
    tbl_name [[AS] alias] lock_type
    [, tbl_name [[AS] alias] lock_type] ...

lock_type: {
    READ [LOCAL]
  | [LOW_PRIORITY] WRITE
}

UNLOCK TABLES

表鎖分爲讀鎖和寫鎖,讀鎖不互斥,可是獲取讀鎖不能寫入數據,其餘沒有獲取到讀鎖的session也是能夠讀取表的,因此讀鎖的目的就是限制表被寫。若是表被讀鎖鎖住後,再執行插入語句會報錯,報錯以下:

1099 - Table 'XXXX' was locked with a READ lock and can't be updated

寫鎖被獲取後能夠對錶進行讀寫,寫鎖是互斥的,一旦某個session獲取到表的寫鎖,另外的session沒法訪問這個表,直到寫鎖被釋放。

表的解鎖可使用unlock tables 解鎖,也能夠客戶端口自動解鎖。lock tables鎖表會獨佔式的鎖住表,除了限制其餘線程對該表的讀寫,也會限制本線程接下來的操做對象。

4、行鎖(InnoDB)

MySQL的行鎖是在引擎層面實現的,因此這裏討論的也是InnoDB引擎下的行鎖,下面會詳細介紹InnoDB下常見的幾種行鎖

4.1 共享鎖

共享鎖能容許事務獲取到鎖後進行讀操做,共享鎖是不互斥的,一個事務獲取到共享鎖後,另一個事務也能夠獲取共享鎖,獲取共享鎖後不能進行寫操做。

4.2 排它鎖

排他鎖容許事務獲取到鎖後進行更新一行或者刪除某一行操做,排他鎖顧名思義是互斥的,一個事務獲取到排他鎖後,其餘事務不能獲取到排他鎖,直到這個鎖被釋放。

4.3 意向鎖

InnoDB支持多種粒度的鎖,容許行鎖和表鎖共存,這裏說的意向鎖實際上是一種表級別的鎖,可是我把它放在行鎖裏面是由於它不會單獨存在,它的出現確定會伴隨着行鎖(共享鎖或者排他鎖),它主要的目的就是表示將要鎖定表中的行或者正在鎖定表中的行。

意向鎖根據和行鎖的組合能夠分爲:

  • 意向排他鎖:代表將要在表中的某些行獲取排他鎖

  • 意向共享鎖:代表將要在表中的某些行獲取共享鎖

意向鎖的獲取必須在行鎖獲取以前,也就是說獲取共享鎖以前必須先要獲取共享意向鎖,對於排他鎖也是同樣的道理。

那麼這個意向鎖到底有什麼做用呢?

解釋這個以前,咱們先看看意向鎖和行鎖以前的兼容關係:

--- 排他鎖(X) 意向排他鎖(IX) 共享鎖(S) 意向共享鎖(IS)
排他鎖(X) 衝突 衝突 衝突 衝突
意向排他鎖(IX) 衝突 兼容 衝突 兼容
共享鎖(S) 衝突 衝突 兼容 兼容
意向共享鎖(IS) 衝突 兼容 兼容 兼容

咱們假設有2個事務A和事務B,事務獲取到了共享鎖,鎖住了表中的某一行,這一行只能讀,不能寫,如今事務B要申請整個表的寫鎖。若是事務B申請成功,那麼確定是能夠對錶中全部的行進行寫操做的,那麼確定與A獲取的行鎖衝突。數據庫爲了不這種衝突,就會進行衝突檢測,那麼如何去檢測呢?有兩種方式:

  • 判斷表是否已經被其餘事務用表級鎖鎖住。
  • 判斷表中的每一行是否被行鎖鎖住。

判斷表中的每一行須要遍歷全部記錄,效率太差,因此數據庫就用第一種方式去作衝突檢測,也就是用到了意向鎖。

總結

本文主要從MySQL的加鎖範圍來分析了MySQL的鎖,MySQL根據加鎖範圍能夠分爲全局鎖、表鎖、行鎖。全局鎖和表鎖是MySQL本身實現,行鎖都是由引擎層面去實現。InnoDB下的行鎖主要分爲共享鎖和排他鎖。共享鎖請求後,行只能讀,共享鎖之間不互斥。排他鎖獲取後能更新和刪除行,排他鎖與其餘鎖都互斥。最後我在行鎖的基礎上提到了意向鎖,意向鎖主要表示正在鎖住行或者即將鎖住行,爲了在鎖衝突檢測中提升效率。固然InnoDB下還有其餘鎖,好比間隙鎖,記錄鎖,Next-Key鎖等,這些都不在本文的探討範圍以內,若有興趣的同窗能夠自行研究。

參考

相關文章
相關標籤/搜索