Innodb中的鎖類型,常見的insert,delete,update如何加鎖,update會死鎖,3個insert可以死鎖?3個delete也會死鎖? ...
latch與lock
latch 可以認為是應用程式中的鎖,可以稱為閂鎖(輕量級的鎖) 因為其要求鎖定的時間必須要非常短,若持續時間長,則會導致應用性能非常差,在InnoDB存儲引擎中,latch又可以分為mutex(互斥鎖)和rwlock(讀寫鎖),其目的用來保證併發線程操作臨界資源的正確性,並且沒有死鎖檢測的機制
在InnoDB存儲引擎中的latch,可以通過命令SHOW ENGINE INNODB MUTEX 來進行查看
mysql > SHOW ENGINE INNODB MUTEX;
lock可以認為是資料庫提供的鎖,用來鎖定的是資料庫中的數據。並且一般lock對象僅在事務commit或rollback後進行釋放(不同事務隔離級別釋放的時間可能不同),lock是有死鎖機制的。
在InnoDB存儲引擎中的lock 可以通過show engine innodb status,information_schema.INNODB_LOCKS,INNODB_TRX,INNODB_LOCK_WATIS信息來查看
線程獲取lock的流程:
在對數據加lock的時候會先對數據所在的頁面添加latch,然後再對數據添加lock,添加完lock後再釋放頁面的Latch。
這種機制主要是為了保證線程獲取的行數據的一致性和完整性.
如果lock被其他的線程占有,線程先釋放頁面latch,等待lock,待獲取lock後會再次對頁面添加latch,查看頁面數據是否有改動,然後嘗試再次獲取對應的lock
共用鎖與排他鎖
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鎖、;
死鎖日誌
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死鎖的出現條件
1、針對唯一索引上等值查詢的刪除
2、有3個以上併發刪除操作
3、事務的隔離級別是RR
4、INNODB儲存引擎
參考文獻
https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html
http://hedengcheng.com/?p=771#_Toc374698320