MySql學習(四)鎖的基礎知識

MySql鎖的介紹

mysql鎖分爲以下幾種
在這裏插入圖片描述

按鎖的功能劃分

共享讀鎖

讀鎖與讀鎖之間是可以共存的即線程A取得讀鎖,線程B也可以取得讀鎖

排他寫鎖

寫鎖與寫鎖以及讀鎖之間是互斥的線程A取得寫鎖,線程B只能等待線程A釋放寫鎖纔可以重新獲得寫鎖或者讀鎖

按鎖的粒度劃分

全局鎖(由MySql layer層也就是之前說的sql server層來實現)

全局鎖就是對整個數據庫實例加鎖。MySQL 提供了一個加全局讀鎖的方法,命令是Flush tables with read lock (FTWRL)。
當你需要讓整個庫處於只讀狀態的時候,可以使用這個命令,之後其他線程的以下語句會被阻塞:數據更新語句(數據的增刪改)、數據定義語句(包括建表、修改表結構等)和更新類事務的提交語句。

實現方式

全局鎖兩種方法

  1. FLUSH TABLES WRITE READ LOCK
  2. set global readonly=true

既然要全庫只讀,爲什麼不使用 set global readonly=true 的方式呢?確實 readonly 方式也可以讓全庫進入只讀狀態,但我還是會建議你用 FTWRL 方式,主要有幾個原因:

一是,在有些系統中,readonly 的值會被用來做其他邏輯,比如用來判斷一個庫是主庫還是備庫。因此,修改 global 變量的方式影響面更大,我不建議你使用。

二是,在異常處理機制上有差異。如果執行FTWRL 命令之後由於客戶端發生異常斷開,那麼 MySQL 會自動釋放這個全局鎖,整個庫回到可以正常更新的狀態。而將整個庫設置爲 readonly 之後,如果客戶端發生異常,則數據庫就會一直保持 readonly 狀態,這樣會導致整個庫長時間處於不可寫狀態,風險較高。

三是,readonly 對super用戶權限無效

注 :業務的更新不只是增刪改數據(DML),還有可能是加字段等修改表結構的操作(DDL)。不論是哪種方法,一個庫被全局鎖上以後,你要對裏面任何一個表做加字段操作,都是會被鎖住的。

即使沒有被全局鎖住,加字段也不是就能一帆風順的,還有表級鎖了

使用場景

全局鎖的典型使用場景是,做全庫邏輯備份(mysqldump)。重新做主從時候
也就是把整庫每個表都 select 出來存成文本。
以前有一種做法,是通過 FTWRL 確保不會有其他線程對數據庫做更新,然後對整個庫做備份。注意,在備份過程中整個庫完全處於只讀狀態。

數據庫只讀狀態的危險性

如果你在主庫上備份,那麼在備份期間都不能執行更新,業務基本上就能停止。
如果你在從庫上備份,那麼備份期間從庫不能執行主庫同步過來的binlog,會導致主從延遲。
注:上面邏輯備份,是不加–single-transaction參數

表鎖

mysql表鎖分爲下面三種

  1. 表鎖
  2. 元數據鎖
  3. 意向鎖

表鎖(由MySql layer層來實現)

表鎖的添加
lock tables 表名 read; # 表示其他線程不能ddl 和 dml 中增刪改,只能讀取該表數據 
lock tables 表名 write; # 表示其他線程既不能讀,也不能寫 

表鎖的語法是 lock tables … read/write。與 FTWRL 類似,可以用 unlock tables
主動釋放鎖,也可以在客戶端斷開的時候自動釋放。需要注意,lock tables
語法除了會限制別的線程的讀寫外,也限定了本線程接下來的操作對象。

舉個例子, 如果在某個線程 A 中執行 lock tables t1 read, t2 write; 這個語句,則其他線程寫 t1、讀寫
t2 的語句都會被阻塞。同時,線程 A 在執行 unlock tables 之前,也只能執行讀 t1、讀寫 t2 的操作。連寫 t1
都不允許,自然也不能訪問其他表。

在還沒有出現更細粒度的鎖的時候,表鎖是最常用的處理併發的方式。而對於 InnoDB 這種支持行鎖的引擎,一般不使用 lock tables
命令來控制併發,畢竟鎖住整個表的影響面還是太大

查看錶鎖
show open tables;
釋放表鎖
unlock tables;
表鎖的使用場景

舉一個最簡單的例子,在統計報表的時候,如果統計的過程中有新的數據添加進來就會很麻煩,這個時候添加讀鎖,所有線程只能讀數據不能寫數據,這樣就能很好的控制。
同步數據的時候也需要加表級鎖。

元數據鎖(meta data lock 由MySql layer層來實現)

元數據鎖是mysql自動添加的爲了防止DDL和DML併發的衝突 ,保證讀寫的正確性。
就是說你查詢一張表的數據的時候自動添加MDL讀鎖,讀鎖與讀鎖之前是不衝突的所以大家都可以查詢這張表,可是如果一個線程在修改表結構,這個時候自動添加MDL寫鎖,這時候所有線程都要等待該事物提交纔可以對這張表進行DDL或者DML操作。

意向鎖(存儲引擎層實現)

意向鎖是一種表鎖,和元數據所以樣無需手動添加

意向鎖的目的

存儲引擎爲了解決表所與行級鎖共存的問題設計了意向鎖。
意向鎖定協議如下:

  1. 在事務可以獲取表中某行的共享鎖之前,它必須首先獲取該表的IS鎖。
  2. 在事務可以獲取表中某行的排它鎖之前,它必須首先獲取該表的IX鎖。

如果一個鎖與現有鎖兼容,則將授予其請求的事務,但如果與現有鎖衝突,則不授予該鎖。事務進行等待直到現有衝突的鎖被釋放。如果請求的鎖與現有鎖定發生衝突,並且由於可能導致死鎖,則會發生錯誤。
鎖之間的衝突關係如下
在這裏插入圖片描述
意向鎖不會阻止除全表請求(例如LOCK TABLES … WRITE)以外的任何內容。意向鎖定的主要目的是表明有人正在鎖定表中的行,或者打算鎖定表中的行。

意向鎖的存在是mysql在RR級別就可以避免幻讀的出現。正常的應該是(Serializable)

舉一個例子

一張表我有1000條數據,我想進行全表更新,那麼我是不是應該首先判斷有沒有別的事物對這張表的某個記錄加行級別的寫鎖。這樣很耗費時間,而有了意向鎖之後,事物A對錶的某條記錄取得行級別的X鎖之前會先取得表級別的IX鎖,然後再取得X鎖。然後事物B要進行全表更新時也要先取得表級別的IX鎖,這時候就會發生互斥然後等待,不用對每條記錄去取得鎖。

行級鎖(由存儲引擎層來實現)

行級鎖鎖的是某條數據或者是數據的間隙(避免幻讀)
InnoDB的行級鎖是通過給索引上的索引項加鎖來實現的。因此只有通過索引條件檢索出來的數據才能加行級鎖,否則將轉爲表鎖

行級鎖分爲下面幾種

  1. 記錄鎖(record locks)

對數據庫裏面某條數據加鎖

  1. 間隙所(gap locks)

對兩條數據之間的間隙加鎖,其實就是對B+TREE中兩個葉子結點加鎖不允許新的節點插入進來。主要是爲了防止幻讀(比如我查詢id > 0 and id < 4 本來是ID = 1 ID =2 兩條數據,但是中間突然插入一條ID=3的數據這個就叫做幻讀)

  1. Next-key locks

是對記錄以及記錄之間的間隙加鎖

而功能上分又分爲

  1. 共享讀鎖(S)
  2. 排他寫鎖(X)

行鎖的添加

讀鎖
select * from table where .... lock in share mode
寫鎖
select * from table where .... for update

間隙鎖的添加

間隙鎖其實和行鎖是一樣的,where條件是等值的話,範圍的話是間隙所

讀鎖
select * from table where .... lock in share mode
寫鎖
select * from table where .... for update

行級鎖與表鎖的區別

表所:開銷小,加鎖快,粒度大,不會出現死鎖,但是併發性低
行級鎖:開銷大,加鎖慢,粒度小,會出現死鎖,但是併發性高

按鎖的實現方式劃分

悲觀鎖

樂觀鎖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章