「鎖「萬千,想要鎖住你的心。

在計算機中,鎖是協調多個進程或線程併發訪問某一資源的一種機制。在數據庫當中,數據也是一種供許多用戶共享訪問的資源。如何保證數據併發訪問的一致性、有效性,是全部數據庫必須解決的一個問題,鎖的衝突也是影響數據庫併發訪問性能的一個重要因素。java

貫穿始終的MDL鎖

  • 什麼是MDL鎖?

MDL鎖的全稱是Meta Data Lock,即元數據鎖。它是MySQL內置級別的鎖,供MySQL預防共享資源衝突的場景。mysql

  • MDL鎖的類型:
鎖名稱 簡稱 鎖類型 說明 使用語句
MDL_INTENTION_EXCLUSIVE S鎖 意向鎖,鎖住一個範圍 任何語句都會獲取MDL意向鎖,而後再獲取更強級別的MDL鎖。
MDL_SHARED S S鎖 共享鎖,表示只訪問表結構
MDL_SHARED_HIGH_PRIO SH S鎖 共享鎖,只訪問表結構 show create table 等
只訪問INFORMATION_SCHEMA的語句
MDL_SHARED_READ SR S鎖 訪問表結構而且讀表數據 select語句
LOCK TABLE ... READ
MDL_SHARED_WRITE SW S鎖 SELECT ... FOR UPDATE
DML語句
MDL_SHARED_UPGRADABLE SU S鎖 可升級鎖,訪問表結構而且讀寫表數據 Alter語句中間過程會使用
MDL_SHARED_NO_WRITE SNW S鎖 可升級鎖,訪問表結構而且讀寫表數據,而且禁止其它事務寫。 Alter語句中間過程會使用
MDL_SHARED_NO_READ_WRITE SNRW S鎖 可升級鎖,訪問表結構而且讀寫表數據,而且禁止其它事務讀寫。 LOCK TABLES ... WRITE
MDL_EXCLUSIVE X X鎖 禁止其它事務讀寫。 CREATE/DROP/RENAME TABLE等DDL語句。

S鎖表明共享鎖,X鎖表明排他鎖。sql

  • MDL的兼容性矩陣(對象維度)
Request type S SH SR SW SU SNW SNRW X
S ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️
SH ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️
SR ✔️ ✔️ ✔️ ✔️ ✔️ ✔️
SW ✔️ ✔️ ✔️ ✔️ ✔️
SU ✔️ ✔️ ✔️ ✔️
SNW ✔️ ✔️ ✔️
SNRW ✔️ ✔️
X

橫向表示其它事務已經持有的鎖,縱向表示事務想加的鎖。數據庫

  • 按對象/範圍維度劃分
屬性 含義 範圍/對象
GLOBAL 全局鎖 範圍
COMMIT 提交保護鎖 範圍
SCHEMA 庫鎖 對象
TABLE 表鎖 對象
FUNCTION 函數鎖 對象
PROCEDURE 存儲過程鎖 對象
TRIGGER 觸發器鎖 對象
EVENT 事件鎖 對象
  • 幾種典型語句的加(釋放)鎖流程圖
  1. select語句操做MDL鎖流程
  2. DML語句操做MDL鎖流程
  3. alter操做MDL鎖流程
  • 幾種典型語句的阻塞分析

注:DMLUPDATEINSERTDELETE);DDLCREATEALTERDROP);DQLSELECT)。緩存

  1. selectalter是否會相互阻塞

當執行select語句時,只要select語句在獲取MDL_SHARED_READ鎖以前,alter沒有執行到rename階段,那麼select獲取MDL_SHARED_READ鎖成功,後續有alter執行到rename階段,請求MDL_EXCLUSIVE鎖時,就會被阻塞。bash

  1. DMLalter是否會相互阻塞

alter在opening階段會將鎖升級到MDL_SHARED_NO_WRITE,rename階段再將升級爲MDL_EXCLUSIVE,因爲MDL_SHARED_NO_WRITEMDL_SHARED_WRITE互斥,因此先執行alter或先執行DML語句,都會致使語句阻塞在opening tables階段。session

  1. selectDML是否會相互阻塞

因爲MDL_SHARED_WRITEMDL_SHARED_READ兼容,因此它們不會由於MDL而致使等待的狀況。併發

按鎖的範圍劃分

全局鎖

  • 全局鎖(FTWRL)是對整個數據庫實例加鎖
    • 加鎖使用命令:
    flush tables with read lock;
    複製代碼
    • 釋放鎖:
    unlock tables
    複製代碼
  • 加鎖流程:
    • 上全局讀鎖(lock_global_read_lock):全部更新操做都會被堵塞
    • 清理表緩存
      1. 關閉全部未使用的表對象
      2. 更新全局字典的版本號
      3. 對於在使用的表對象,逐一檢查,若表還在使用中,調用MDL_wait::timed_wait進行等待
      4. 將等待對象關聯到table_cache對象中
      5. 繼續遍歷使用的表對象
      6. 直到全部表都再也不使用,則關閉成功。
    • 上全局COMMIT鎖(make_global_read_lock_block_commit):會堵塞活躍事務提交

全局鎖的典型使用場景是,作全庫邏輯備份。把整庫每一個表都select出來存成文本。若是不加鎖會不會出現問題呢?函數

表數據變動狀態 備份狀態
account表a用戶有200元餘額account(a,200)
course表沒有任何數據
備份account表 獲得account(a,200)
用戶買了一門Java課,花了100元。
account表a用戶有100元餘額
course表數據爲course(a,java)
這個時候我備份跑到course表了,那麼備份結果就是course(a,java)

獲得最終備份結果即是account(a,200) course(a,java),這顯然是不對的,由於不加鎖的話中間有業務變動,所得數據是不一致。工具

  • 按照上面說的,咱們只須要備份開始,若是數據庫的引擎是InnoDB,隔離級別在RR下,開啓個事務,就能拿到一致性視圖。
  • MySQL官方提供的備份工具mysqldump

mysqldump 使用參數–single-transaction 的時候,導數據以前就會啓動一個事務,來確保拿到一致性視圖。

表級鎖

MySQL 裏面表級別的鎖有兩種:一種是表鎖,一種是MDL鎖(TABLE範圍)。

  • 表鎖的語法是 lock tables … read/write。與 FTWRL 相似,能夠用 unlock tables 主動釋放鎖,也能夠在客戶端斷開的時候自動釋放。
  • MDL鎖,當屬性爲TABLE,做用範圍爲表級別的時候,它也是一把表鎖。正如咱們上面幾種典型語句的加(釋放)鎖分析的過程當中那樣。它不須要顯示的使用,由於MySQL會根據你的執行語句來分析是否加鎖和加何種鎖。

行鎖(Record Lock)

  • 行鎖的功與過

    • 兩階段鎖協議

    兩階段鎖協議:在InnoDB事務中,行鎖是須要時候才加鎖,但不會不須要了就釋放掉,而是等待事務提交結束時才釋放。

    • 根據行鎖的兩階段鎖協議特性優化代碼

    如圖多個用戶都點擊下單的時候,產生鎖競爭的主要場所是影院帳戶餘額新增票價,兩階段鎖協議特性是事務結束才釋放鎖,那麼將這步驟放在最後是鎖暫用時間最短。

    • 死鎖問題
事務A 事務B
update t set k = k+1 where id = 1;
update t set k = k+3 where id = 2;
update t set k = k+1 where id = 2;
update t set k = k+5 where id = 1;

上面會出現死鎖,解決方式即是按順序加鎖來避免死鎖。

事務A 事務B
update t set k = k+1 where id = 1;
update t set k = k+3 where id = 1;
update t set k = k+1 where id = 2;
update t set k = k+5 where id = 2;
  • 行鎖的類別
    • 讀鎖(S)
    • 寫鎖(X)
  • 行鎖的加鎖策略

對於insertupdatedelete操做,InnoDB會自動給涉及到的數據加排他鎖,只有select須要咱們手動設置加鎖級別。

  • 行鎖的加鎖語句
-- 讀鎖(S鎖)
select * from t where id = 1 lock in share mode;
-- 寫鎖(X鎖)
select * from t where id = 1 for update;
複製代碼

Record Lock、Gap Lock與Next-Key Lock加鎖圖析

  • 回顧下InnoDB的主鍵索引和輔助索引 主鍵索引(聚簇)

輔助索引

  • Record Lock加鎖策略

對於主鍵索引,會在主鍵索引標上鎖標記。對於普通索引,不僅在普通索引標上鎖標記,並且也會在主鍵索引標上。

  • Gap Lock加鎖策略

間隙鎖它鎖的是索引與索引之間的間隙。

  • Next-Key Lock加鎖策略

由圖能夠發現Next-Key Lock等於Record Lock加上Gap Lock。左開右閉。

Next-Key Lock的須要知道的幾個小事

  • 兩原則、兩優化
    原則1:加鎖的基本單位是Next-Key Lock原則2:查找過程當中訪問到的對象纔會加鎖。 優化1:索引上的等值查詢,給惟一索引加鎖的時候,Next-Key Lock 退化爲行鎖。 優化2:索引上的等值查詢,向右遍歷時且最後一個值不知足等值條件的時候,Next-Key Lock 退化爲間隙鎖。

寫到這,須要明確一個事兒,Next-Key LockInnoDB RR隔離級別下的鎖,是內置鎖,是MySQL幫我解決某種場景鎖引入的鎖,那麼這個場景是什麼?其實它想解決的是某種狀況下的幻讀場景

幻讀場景分析

幻讀僅專指「新插入的行

  • 當前讀場景下的幻讀 表t的結構與數據
id c d(key)
0 0 0
5 5 5
10 10 10
15 15 15
20 20 20
25 25 25

再看下面這個場景:

session A sessionB
T1 begin;
update t set d=100 where d=5
T2 insert into t values(1,1,5);
T3 commit;
  • 爲何這種狀況會被稱爲幻讀,幻讀有什麼問題?
  1. 語義上問題:
select * from t where d = 5 for update
複製代碼

由於本想鎖住d=5,這句話的語義被破壞了,「新插入」了一行新的d=5的數據(1,1,5)。

  1. 數據一致性的問題: 上面的狀況執行結果:
id c d(key)
0 0 0
5 5 100
10 10 10
15 15 15
20 20 20
25 25 25
1 1 5

咱們再分析下bin log中的記錄內容

insert into t values(1,1,5);
update t set d=100 where d=5;
複製代碼

能夠發現bin log發生與原執行不一樣的結果,出現了數據不一致。爲了解決這個問題,InnoDB RR級別下,行鎖並不會阻止當前讀狀況下的幻讀問題,才引入了上面所提到的Next-Key Lock

寫到這,還須要明確的一件事Next-Key Lock只會阻止往當前範圍的insert動做。並且間隙鎖是內置鎖,InnoDB RR級別的行鎖默認加的。

相關文章
相關標籤/搜索