這幾天,在查看文章時,發現了一個Mysql併發的問題,在一開始僅僅憑藉眼睛去查看時,並未發現問題及解決方法,因而咱們對其進行了具體實際操做和測試:
一個事務內:insert記錄後根據字段p來update這條記錄,然而當出現併發操做的時候,update處會發生dead lock問題,把update改成id,就沒事了。sql
同一個表,高併發事務,事務內先插入一條記錄,再更新這條記錄: (1)若是更新的是惟一索引,有異常; (2)若是更新的是自增主鍵,就沒有異常; 畫外音:先不要被「dead lock」描述所迷惑,是死鎖問題,阻塞問題,仍是其餘異常,還另說。session
create table t (id int(20) primary key AUTO_INCREMENT,cell varchar(20) unique)engine=innodb;
複製代碼
新建表: (1)存儲引擎是innodb,MySQL版本是5.6; (2)id字段,自增主鍵; (3)cell字段,惟一索引;併發
start transaction;
insert into t(cell) values(11111111111);
insert into t(cell) values(22222222222);
insert into t(cell) values(33333333333);
commit;
複製代碼
插入一些測試數據。高併發
設置事務隔離級別爲RR(repeatable read)測試
--設置手動提交
--設置事務隔離級別爲RR
set session autocommit=0;
set session transaction isolation level repeatable read;
複製代碼
多個終端session模擬併發事務 ui
start TRANSACTION;
INSERT INTO t(cell) VALUES(44444444);
UPDATE t set cell = 123 WHERE cell = 44444444 ;
ROLLBACK;
複製代碼
start TRANSACTION;
INSERT INTO t(cell) VALUES(5555555);
UPDATE t set cell= 456 WHERE cell = 5555555 ;
ROLLBACK;
複製代碼
在Navicat中開啓兩個窗口spa
奇怪的出現了!code
按道理,插入不衝突的記錄,而後修改這條記錄,行鎖不該該衝突呀?
惟一索引,主鍵索引怎麼會有差別呢?是否有關?是死鎖,仍是其餘緣由?orm
百思不得其解,那就先看看innodb status裏都有什麼吧,複製粘貼下來後查看: cdn
可見Transaction1與Transaction2 同時鎖住了同一部分,並且是locak_mode X rec bur not gap Record lock
這就很奇怪了,又不是間隙鎖引發的死鎖,第一次update爲何會等待呢,第二次update爲啥會死鎖呢?
不懂,就換個地方看看
經過查看information_schema庫中inndb_locks表,可看到,確實事務1和事務2,同時鎖住了一片數據區域,致使了數據的等待、死鎖,可是緣由呢?
因而再次換方法查看:
咦!發現了重大問題
爲何這裏rows竟然是6!
我update爲何會掃了全表??
我是加了索引的啊
找到問題了:update沒走索引,而是掃了全表!
既然找到問題了,就看看如何解決,爲何update沒有走索引呢? 那咱們回頭再看看兩個update語句
UPDATE t set cell = 123 WHERE cell = 44444444 ;
UPDATE t set cell= 456 WHERE cell = 5555555 ;
複製代碼
看着是沒啥問題呀?
尋尋膩膩,冷冷清清,悽悽慘慘慼戚,終於,在查看錶時,發現了問題:
回頭看建表語句/表結構
create table t (id int(20) primary key AUTO_INCREMENT,cell varchar(20) unique)engine=innodb;
複製代碼
cell字段數據類型是varchar類型的,而咱們的update寫的是cell = 444444;
並未對數據加引號!而致使了update沒走索引,掃了全表
因而,咱們再從頭看看這個過程:
在事務隔離級別爲RR(Repeat Read)下
事務1的insert產生了一個插入意向鎖,事務2的insert也產生了一個插入意向鎖(不會被互相鎖住,由於數據行並不衝突)
此時事務1再進行update語句,因未走索引,致使掃全表,而在掃到事務2插入那條數據時,行鎖與插入意向鎖衝突了,致使事務1須要等待事務2釋放插入意向鎖而進行等待。
事務2在進行update時,也一樣須要掃全表,可是全表都被事務1的update鎖住了,事務2須要等待 等待事務2釋放插入意向鎖的 事務1 的行鎖 釋放,所以發生了死鎖
那解決方法就很簡單了,將語句改成:
UPDATE t set cell = "123" WHERE cell = "44444444" ;
UPDATE t set cell= "456" WHERE cell = "5555555" ;
複製代碼
便可解決死鎖/等待問題
其實在進行測試時,也曾經懷疑過是否是由於RR的問題,改爲RC試試呢?
--將事務隔離級別改成RC
SET TRANSACTION ISOLATION LEVEL REPEATABLE COMMITTED;
複製代碼
修改後,對其進行相同的操做:
發現:事務1insert,事務2insert,事務1的update生效,事務2的update發生了等待
根據上文中咱們找到的問題,對其進行分析:
獲得結論:
RC下不存在間隙鎖
對於RC和RR的比較,咱們對錶中數據採起了刪數據的方法繼續進行測試:
truncate table
複製代碼
繼續對錶進行相同操做,結果:
究其緣由,RR下,事務1插入的數據,事務2能看到,所以在RR下,即便數據清空,事務1仍然鎖住了事務2插入的數據。
而在RC下,事務1插入的數據事務2看不到,事務2插入的數據事務1看不到,他們各自僅僅鎖住了本身插入的數據,所以能執行成功。
拋開可重複讀和讀已提交他們在同一個事務中屢次讀可讀出的東西外,這次發現了他們其餘的不一樣:
在這次過程當中,咱們發現了: 行鎖、間隙鎖、插入意向鎖。其中因不一樣的行爲產生了不一樣的鎖,而其意義、用處也是不一樣的:
在咱們處理sql語句去執行時,不一樣的語句會選擇不一樣的鎖:
若是更新條件沒有走索引,例如執行」update test set name=「hello」 where name=「world」;」 ,此時會進行全表掃描,掃表的時候,要阻止其餘任何的更新操做,因此上升爲表鎖。
若是更新條件爲索引字段,可是並不是惟一索引(包括主鍵索引),例如執行「update test set name=「hello」 where code=9;」 那麼此時更新會使用Next-Key Lock。使用Next-Key Lock的緣由:
首先要保證在符合條件的記錄上加上排他鎖,會鎖定當前非惟一索引和對應的主鍵索引的值;
還要保證鎖定的區間不能插入新的數據。
若是更新條件爲惟一索引,則使用Record Lock(記錄鎖)。