Mysql Innodb 鎖機制

latch與lock

latch 能夠認爲是應用程序中的鎖,能夠稱爲閂鎖(輕量級的鎖) 由於其要求鎖定的時間必需要很是短,若持續時間長,則會致使應用性能很是差,在InnoDB存儲引擎中,latch又能夠分爲mutex(互斥鎖)和rwlock(讀寫鎖),其目的用來保證併發線程操做臨界資源的正確性,而且沒有死鎖檢測的機制html

 

在InnoDB存儲引擎中的latch,能夠經過命令SHOW ENGINE INNODB MUTEX 來進行查看mysql

mysql > SHOW ENGINE INNODB MUTEX;算法

 

NewImage 

 

lock能夠認爲是數據庫提供的鎖,用來鎖定的是數據庫中的數據。而且通常lock對象僅在事務commit或rollback後進行釋放(不一樣事務隔離級別釋放的時間可能不一樣),lock是有死鎖機制的。sql

在InnoDB存儲引擎中的lock 能夠經過show engine innodb status,information_schema.INNODB_LOCKS,INNODB_TRX,INNODB_LOCK_WATIS信息來查看數據庫

NewImage 

線程獲取lock的流程:併發

在對數據加lock的時候會先對數據所在的頁面添加latch,而後再對數據添加lock,添加完lock後再釋放頁面的Latch。mvc

這種機制主要是爲了保證線程獲取的行數據的一致性和完整性.性能

若是lock被其餘的線程佔有,線程先釋放頁面latch,等待lock,待獲取lock後會再次對頁面添加latch,查看頁面數據是否有改動,而後嘗試再次獲取對應的lockspa

 

 

共享鎖與排他鎖

innodb儲存引擎提供了以下兩種標準的行級鎖線程

共享鎖(S) 容許一個事務去讀一行

排他鎖(X) 容許得到排他鎖的事務更新或刪除數據

 

同時innodb儲存引擎支持多粒度鎖定,爲了支持在不一樣的粒度上進行加鎖操做,innodb支持另外一種額外的鎖方式,稱之爲意向鎖

 

意向共享鎖(IS)  事務想要得到一張表中某幾行的共享鎖

意向排他鎖(IX)事務想要得到一張表中某幾行的排他鎖

 

在行鎖的實現上

mysql提供了三種的行鎖的算法

分別是

Record Lock 記錄鎖,單個記錄上的鎖

Gap Lock 間隙鎖,鎖定一個範圍,但不包含記錄自己

Next-key Lock Gap Lock + Record Lock 鎖定一個範圍,而且鎖定記錄自己

 

 

Mysql是如何加鎖的

非特殊註明 默認在RR隔離級別下進行討論

InnoDb的行鎖是對索引加鎖的,對掃描的行邊掃描邊加鎖,若是走的是二級索引(非聚簇索引)除了須要對二級索引加鎖外,還須要根據二級索引裏面的主鍵信息掃描主鍵的聚簇索引,對主鍵加鎖,

 加鎖的數據行數會受到Mysql是否支持Index Condition PushDown而影響(Mysql 5.6支持ICP),加鎖的數量可能遠遠大於知足條件的記錄數量

這裏須要加兩次鎖的緣由是

若是

語句A 使用二級索引對記錄X進行更新操做,

語句B使用聚簇索引對記錄X進行更新操做,

若是A僅對二級索引進行加鎖,那麼併發的語句B將感覺不到語句A的存在,違背了同一條記錄上的更新/刪除必須串行執行的約束

select * from table where?

RC級別下 : 無需加鎖,一致性非鎖定讀,使用快照讀,讀取被鎖定行的最新一份數據,所以會出現先後讀取數據不一致的狀況

RR級別下:無需加鎖,一致性非鎖定讀,使用快照讀,讀取事務開始時的行數據版本,所以先後讀到的數據是同樣的

Serializable級別下:使用當前讀,須要加鎖,innodb內部將select語句轉換爲了select … lock in share mode

 

insert?

insert會對插入成功的行加上記錄鎖,不會阻止其餘併發的事務往這條記錄以前插入記錄。在插入以前,會先在插入記錄所在的間隙加上一個插入意向意向鎖(併發的事務能夠對同一個間隙加插入意向鎖鎖)。若是insert 的事務出現了duplicate-key error ,事務會對duplicate index record的記錄加共享鎖。這個共享鎖在併發的狀況下是會產生死鎖的,好比有兩個併發的insert都對要對同一條記錄加共享鎖,而此時這條記錄又被其餘事務加上了排它鎖,排它鎖的事務將這條記錄刪除後,兩個併發的insert操做會發生死鎖。

 

delete?

delete操做僅是將主鍵列中對對應的記錄delete flag設置爲1,記錄並無被刪除,仍是存在於B+樹中

真正的刪除操做被延遲了,最終在purge操做中完成

延遲到purge操做的緣由是的innodb支持mvcc多版本控制,因此記錄不能在事務提交時當即進行刪除,只有當對應的行記錄不被任何其餘事務引用的時候,才能夠由purge進行真正的刪除

delete操做過程當中:

找到知足條件的記錄,而且記錄有效,則對記錄加X鎖

找到知足條件的記錄,可是記錄無效(標識爲刪除),則對記錄加next key鎖、;

未找到知足條件的記錄,則對第一個不知足條件的記錄加Gap鎖,保證沒有知足條件的記錄插入;

 

update?

對知足條件的記錄next-key鎖,若是是等值匹配而且使用惟一索引或是聚簇索引,那麼能夠只添加記錄鎖

 

 

惟一索引中含NULL值的記錄,將不會添加記錄鎖,轉而爲next-key鎖 由於NULL不等於NULL,NULL和任何值比較均返回NULL,包括NULL自己,可是 NULL is NULL

 

死鎖案例分析

create table `deadlocktest`

(

`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

`a` bigint(20) unsigned NOT NULL,

`b` bigint(20) unsigned NOT NULL,

`c` bigint(20) unsigned NOT NULL,

`d` bigint(20) unsigned NOT NULL,

`e` bigint(20) unsigned NOT NULL,

PRIMARY KEY(`id`),

UNIQUE KEY `I_a`(`a`),

KEY `I_b` (`b`),

KEY `I_c` (`c`)

)ENGINE=InnoDb ;

 

insert into deadlocktest (a,b,c,d,e)values(1,999,3,4,5);

insert into deadlocktest (a,b,c,d,e)values(2,998,4,5,6);

insert into deadlocktest (a,b,c,d,e)values(3,997,4,5,6);

insert into deadlocktest (a,b,c,d,e)values(4,996,3,4,5);

...

insert into deadlocktest (a,b,c,d,e)values(1000,1,3,4,5);

 

3個insert的死鎖

事務A

事務B

事務C

begin;

begin;

begin;

insert into deadlocktest (a,b,c,d,e)values(4,996,3,4,5);

 

 

 

insert into deadlocktest (a,b,c,d,e)values(4,996,3,4,5);

 

 

 

insert into deadlocktest (a,b,c,d,e)values(4,996,3,4,5);

rollback;

 

 

 

1 row affected

Deadlock found when trying to get lock; try restarting transaction

 

事務A 得到排他鎖,插入數據成功

事務B 事務C,由於記錄duplicate-key error轉而持有行的共享鎖

事務A回滾,釋放了持有的排他鎖,事務B和事務C須要得到該行的排他鎖,可是因爲互相都持有對應行的共享鎖,互相等待,形成死鎖

 

2個update的死鎖

事務A

事務B

begin;

begin;

update deadlocktest force index(I_b) set e = sleep(5) where b>0;

 

 

update deadlocktest force index(I_c) set e = sleep(5) where c>2;

Deadlock found when trying to get lock; try restarting transaction

Rows matched: 4  Changed: 4  Warnings: 0

兩個update事務,加鎖順序不同致使的死鎖

InnoDb的行鎖是對索引加鎖的,對掃描的行邊掃描邊加鎖,若是走的是二級索引(非聚簇索引)除了須要對二級索引加鎖外,還須要根據二級索引裏面的主鍵信息掃描主鍵的聚簇索引,對主鍵加鎖

3個以上delete的死鎖

事務A

事務B

事務B

begin;

begin;

 begin

delete from deadlocktest where a=550

 

 

 

delete from deadlocktest where a=550

 
 

 

 

delete from deadlocktest where a=550

commit; 0 rows affected  Deadlock found when trying to get lock; try restarting transaction

delete操做僅是將主鍵列中對對應的記錄delete flag設置爲1,實際的刪除延遲到purge中

delete刪除時若是找到知足條件的記錄,可是記錄無效(標識爲刪除),則對記錄加next key鎖、;

死鎖日誌

NewImage

3個delete的死鎖比較難以復現,我是利用以下腳本完成的

MY_DB="mysql -hxxx -Pxxx -uxxx -pxxx"

while :
do
echo "use test;begin; delete from deadlocktest where a=499;rollback;" | $MY_DB
done

該類delete死鎖的出現條件

一、針對惟一索引上等值查詢的刪除

二、有3個以上併發刪除操做

三、事務的隔離級別是RR

四、INNODB儲存引擎

 

參考文獻

https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html

http://hedengcheng.com/?p=771#_Toc374698320

http://hedengcheng.com/?p=844

相關文章
相關標籤/搜索