Mysql加鎖與實踐

1. 鎖分類

innodb中的鎖分爲S鎖,即共享鎖,另外一種爲X鎖,排它鎖,好比:html

共享鎖(S)mysql

select * from supplier where id=5 lock in share mode;
複製代碼

排他鎖(X)sql

select * from supplier where id=5 for update;
複製代碼

或者insert,delete,update語句,這都是排他鎖spa

兼容性3d

這兩種鎖的兼容以下:日誌

X S
X N N
S N Y

意向鎖code

能夠理解爲屬於S鎖和X鎖的父節點,即要獲取S鎖或者X鎖的話,必須先提早獲取意向鎖,即IS或者IX鎖,那綜合起來看下,這兩類鎖的兼容狀況以下cdn

X S IX IS
X N N N N
S N Y N Y
IX N N Y Y
IS N Y Y Y

隱式鎖htm

也是醉了,爲何要搞這麼多概念。。這種鎖,能夠認爲不衝突的時候不加鎖,這個時候的鎖就是隱式鎖,等遇到衝突了,該鎖會升級爲顯示鎖,仍是經過一個例子來講明吧:blog

事務1(其中age是普通索引)

select * from test01 where age=21 for update;
複製代碼

咱們看到mysql中實際上是沒有找到鎖的:

事務2

insert into test01(id,name,age) values(8,'zzh',22);
複製代碼

這個時候因爲[21,23)有gap鎖,因此事務2會被阻塞住,這個時候再看mysql中的鎖記錄

能夠看到一開始事務1雖然使用的是...for update,可是因爲沒有衝突因此加的是隱式鎖,等到事務2開始致使有衝突存在,因此事務1的鎖改成現實鎖。

2.加鎖分析與實踐

具體在事務的不一樣隔離級別下,不一樣的場景加鎖分析,在hedecheng加鎖分析這篇文章已經講解的很是詳細了,這裏就再也不多說了。主要說下其餘狀況下總結的一些加鎖分析。

2.1 插入意向鎖(Insert Intention Locks)

先來看官網對該鎖的解釋

咱們重點關注下紅色的區域:在遇到衝突的時候,會在該索引所在的位置加S鎖,而該共享鎖又很容易致使死鎖,官網中就有個例子來專門說明這種共享鎖致使的死鎖,即三個事務同事執行一條插入語句其中一個事務回滾致使死鎖:

咱們能夠簡單的把這種狀況模仿一遍:

事務1

insert into test01(id,name,age)values(12,"zzh",33);
複製代碼

事務2

insert into test01(id,name,age)values(12,"zzh",33);
複製代碼

事務3

insert into test01(id,name,age)values(12,"zzh",33);
複製代碼

以下圖操做:

咱們看下Mysql中鎖的記錄

其中這個就印證了在乎向鎖衝突的時候,請求加的是S鎖,而後咱們回滾事務1

事務1

rollback
複製代碼

這個時候咱們看到事務2成功了,事務3出現死鎖

咱們能夠從死鎖日誌中看到:

  • 事務2:等待鎖模式爲X的插入意向鎖
  • 事務3:等待鎖模式爲X的插入意向鎖;擁有鎖模式爲S的record lock
  • 暗含條件:事務2也擁有鎖模式爲S的record lock,這才致使了死鎖,死鎖日誌會把最終成功獲取鎖了的事務已經擁有的鎖不會打印出來

因此存在 事務2->事務3,事務3->事務2 死鎖發生

官網中還要另外一個相似的例子,這裏就很少作分析了,緣由相似。

下面咱們看下另外一種狀況:

事務1(事務id=2944)

select * from test01 where age=21 for update;
複製代碼

事務2(事務id=2945)

insert into test01(id,name,age)values(2,"zzh",22);
複製代碼

事務3(事務id=2946)

select * from test01 where age=21 lock in share mode;
複製代碼

執行以下

mysql中鎖記錄

咱們能夠看到

  • 事務1:在primary key的(X,RECORD LOCK),在age_idx的(X,RECORD LOCK)
  • 事務2:在primay key 的(S,RECORD LOCK),這個就是咱們前面講到的,事務2在獲取插入意向鎖出現衝突,因此阻塞在了要獲取(S,RECORD LOCK)
  • 事務3:在age_idx的(S,RECORD LOCK),

而後咱們提交事務1 事務1

commit;
複製代碼

  • 事務2:加S鎖成功,可是出現惟一鍵衝突,直接報錯,這裏有個很重要的一點是,在事務對該行成功加S鎖以後發現記錄存在的狀況下,會直接報錯,可是雖然報錯,S鎖不會釋放
  • 事務3:獲取S鎖成功,可是是在age_idx上,跟事務2不同。

咱們再從新執行事務1

事務1(事務id=29467)

select * from test01 where age=21 for update;
複製代碼

執行結果被阻塞:

咱們看下mysql中的鎖記錄:
能夠看到事務3擁有在age_idx的S鎖,阻塞了事務1要獲取的X鎖,那這個時候咱們提交事務3 事務3 commit;

咱們發現事務1還處於阻塞的狀態,看下mysql中的鎖記錄

這個時候明白了, 事務2雖然執行失敗,可是其因爲插入意向鎖衝突所加的S鎖並未釋放,因此會致使事務1還處於阻塞中。只有當咱們提交了事務2,事務1纔會真正執行。

2.2 select...for update

該語句加鎖分爲兩種狀況

  • 記錄存在:若是是RC模式下,加的是(X,RECORD LOCK),RR 模式下,加的是(X,NEXT-KEY LOCK),該鎖相互不兼容
  • 記錄不存在:加(X,GAP)鎖,而且該鎖是兼容的,可是與Insert Intention Locks 不兼容,這個很容易致使死鎖

2.2.1 不一樣索引之間等待出現死鎖

事務1

select * from test01 where age=21 for update;
複製代碼

事務2

insert into test01(id,name,age)values(3,"zzh",22);
複製代碼

事務1

insert into test01(id,name,age)values(3,"zzh",100);
複製代碼

按如上順序執行結果:

咱們發現出現死鎖,事務2被mysql回滾以後事務1執行成功。咱們看下具體的死鎖日誌

經過死鎖日誌咱們能夠分析出來

  • 事務1: 擁有age_idx(X,NEXT-KEY LOCK),加插入意向鎖存在衝突,因此等待id=3位置的(S,RECORD LOCK)
  • 事務2: 等待age_idx的插入意向鎖(插入意向鎖不僅是在primary key上纔有)
  • 暗含條件:事務2雖然在age_idx上等待插入意向鎖,可是在id=3位置上加插入意向鎖成功了,這纔有了事務1在該位置加插入意向鎖存在衝突

2.2.2 記錄不存在致使的死鎖

事務1(事務id=29684)

select * from test01 where age=21 for update;
複製代碼

事務2(事務id=29683)

select * from test01 where age=21 for update;
複製代碼

事務1

insert into test01(id,name,age)values(3,"zzh",22);
複製代碼

執行結果以下

能夠看到

  • 事務1和事務2同時持有了age=22這個記錄的gap鎖,因爲記錄不存在,因此此時的gap鎖兼容
  • 可是記錄不存在兼容的gap鎖和插入意向鎖兵不兼容,因此事務1向age=22索引所請求的插入意向鎖會等待。

事務2

insert into test01(id,name,age)values(3,"zzh",22);
複製代碼

由上面的分析一樣得出事務2的等待有兩種狀況:

(1) 和事務1同樣,等待age=22的插入意向鎖,此時發現已經有事務1在等待該位置的插入意向鎖,那就等待該位置的S鎖

(2) 事務1雖然在age=22的插入意向鎖等待,可是id=3的插入意向鎖是加成功了,因此若是事務2在id=3這裏等待插入意向鎖的話,也會有衝突,那就等待該位置的S鎖

因此,有這兩種狀況都會致使事務出現死鎖,咱們具體看下死鎖日誌:

咱們看到:

  • 事務1在等待age=22位置的插入意向鎖
  • 事務2在等待 id=3位置的S鎖,也就是咱們分析的第二種狀況,同時事務2擁有age=22位置的gap鎖

咱們上面有分析,事務2的等待應該是有兩種狀況,而出現這兩種狀況的可能就是事務2的這條語句

insert into test01(id,name,age)values(3,"zzh",22);
複製代碼

咱們瞭解到在記錄不存在的時候,若是對此記錄進行select...for update語句,該語句會對空位置加gap鎖,這個會比較危險,若是咱們的表用的是自增主鍵,而此時若是查詢一個不存在的記錄,那會把將來要插入的全部的空隙都加了gap鎖,會致使之後表中沒法在插入任何數據,這個極其危險。

3. 小結

本篇,咱們介紹了mysql中的鎖以及在實踐中可能遇到的一些死鎖,專門經過幾個demo集中分析了一下,對於加鎖的分析,在文中所提到的hedecheng的文章中,已經有很深的講解,因此本文並無對此總結。重點仍是經過一些實際中用到的例子進行實際的分析一下整個過程。重點是插入意向鎖和select...for update中的一些加鎖的分析。下一篇來專門介紹下mysql的MVCC機制

相關文章
相關標籤/搜索