本文探討Mysql 5.6數據庫事務隔離級別read committd/repeatable read對InnoDB引擎下select in update的影響。
首先,這兩種事務隔離級別都不會阻塞普通的查詢語句。
但是在update語句中用到select時,如果該會話的事務隔離級別為repeatable?read,且select的目標行上有排它鎖,就會讓select產生等待。
在此種情況下,如果在本地開啟事務先執行了update?A,又調用遠程服務執行select?A?in?update?B,即會產生死鎖。
舉例說明:
準備測試數據:
CREATETABLET_A?(
A_IDINTPRIMARYKEY,?A_VALUEVARCHAR(10)
)?ENGINE=InnoDBDEFAULTCHARSET=utf8;
CREATETABLET_B?(
B_IDINTPRIMARYKEY,?B_VALUEVARCHAR(10)
)?ENGINE=InnoDBDEFAULTCHARSET=utf8;
INSERTINTOT_ASELECT1,'VA1';
INSERTINTOT_BSELECT1,'VB1';
Step?1:
打開查詢分析器,先檢查一下事務隔離級別
SELECT@@global.tx_isolation;
SELECT@@session.tx_isolation;
設置自動提交為false
SETAUTOCOMMIT?=0;
更新A表一條數據,不提交
UPDATET_ASETA_VALUE?=1WHEREA_ID?=1;
Step?2:
在另一個客戶端中打開同一個數據庫的連接
驗證當前的事務隔離級別是否為REPEATABLE?READ或READ?COMMITTED
SELECT@@session.tx_isolation;
無論何種事務隔離級別,執行對A表的查詢,都可以順利查詢出來
SELECT*FROMT_AWHEREA_ID?=1;
更改當前事務隔離級別為READ?COMMITTED
SETSESSION?TRANSACTIONISOLATIONLEVELREADCOMMITTED;
執行一個select?in?update
UPDATET_BSETB_VALUE?=?(SELECTA_VALUEFROMT_AWHEREA_ID?=?1)WHEREB_ID?=1;
此時可以更新目標行
更改當前事務隔離級別為REPEATABLE?READ
SETSESSION?TRANSACTIONISOLATIONLEVEL?REPEATABLE?READ;
再次執行上面的select?in?update
UPDATET_BSETB_VALUE?=?(SELECTA_VALUEFROMT_AWHEREA_ID?=?1)WHEREB_ID?=1;
此時更新被阻塞,在連接1中提交事務,此更新語句即可繼續執行
同樣的,如果連接1中的事務未提交,在連接2中嘗試在A表上對A_ID=1的行獲取共享鎖或排它鎖也是不可行的
SELECT*FROMT_AWHEREA_ID?=1LOCKINSHARE?MODE;
SELECT*FROMT_AWHEREA_ID?=1FORUPDATE;
都會等待該行數據的鎖被釋放
在REPEATABLE?READ模式下產生阻塞時分析鎖狀態:
CREATETABLEinnodb_lock_monitor?(aINT)?ENGINE=INNODB;
然后讓select?in?update產生等待,分析鎖狀態
SHOWENGINE?INNODB?STATUS;
--事務2獲取T_A主鍵上的共享鎖產生等待
---TRANSACTION?222643,?ACTIVE?11?sec?starting?index?read
mysql?tables?in?use?2,?locked?2
LOCK?WAIT?4?lock?struct(s),?heap?size?1184,?2?row?lock(s)
MySQL?thread?id?49,?OS?thread?handle?0x7f2b58644700,?query?id?1061?192.168.50.57?root?statistics
UPDATE?T_B?SET?B_VALUE?=?(SELECT?A_VALUE?FROM?T_A?WHERE?A_ID?=?1)?WHERE?B_ID?=?1
Trx?read?view?will?not?see?trx?with?id?>=?222644,?sees?<?222642
-------?TRX?HAS?BEEN?WAITING?11?SEC?FOR?THIS?LOCK?TO?BE?GRANTED:
RECORD?LOCKS?space?id?1611?page?no?3?n?bits?72?index?`PRIMARY`?of?table?`lovol_shop`.`T_A`?trx?id?222643?lock?mode?S?locks?rec?but?not?gapwaiting
Record?lock,?heap?no?2?PHYSICAL?RECORD:?n_fields?4;?compact?format;?info?bits?0
0:?len?4;?hex?80000001;?asc?????;;
1:?len?6;?hex?0000000365b2;?asc?????e?;;
2:?len?7;?hex?770000025617ba;?asc?w???V??;;
3:?len?1;?hex?31;?asc?1;;
------------------
TABLE?LOCK?table?`lovol_shop`.`T_B`?trx?id?222643?lock?mode?IX
RECORD?LOCKS?space?id?1612?page?no?3?n?bits?72?index?`PRIMARY`?of?table?`lovol_shop`.`T_B`?trx?id?222643?lock_mode?X?locks?rec?but?not?gap--事務2獲得了B表的排它鎖
Record?lock,?heap?no?2?PHYSICAL?RECORD:?n_fields?4;?compact?format;?info?bits?0
0:?len?4;?hex?80000001;?asc?????;;
1:?len?6;?hex?0000000365b0;?asc?????e?;;
2:?len?7;?hex?f5000001c0011d;?asc????????;;
3:?len?3;?hex?564231;?asc?VB1;;
TABLE?LOCK?table?`lovol_shop`.`T_A`?trx?id?222643?lock?mode?IS
RECORD?LOCKS?space?id?1611?page?no?3?n?bits?72?index?`PRIMARY`?of?table?`lovol_shop`.`T_A`?trx?id?222643?lock?mode?S?locks?rec?but?not?gapwaiting
Record?lock,?heap?no?2?PHYSICAL?RECORD:?n_fields?4;?compact?format;?info?bits?0
0:?len?4;?hex?80000001;?asc?????;;
1:?len?6;?hex?0000000365b2;?asc?????e?;;
2:?len?7;?hex?770000025617ba;?asc?w???V??;;
3:?len?1;?hex?31;?asc?1;;
--事務1獲得了表A主鍵上的排它鎖,但是未提交
---TRANSACTION?222642,?ACTIVE?43?sec
2?lock?struct(s),?heap?size?360,?1?row?lock(s),?undo?log?entries?1
MySQL?thread?id?35,?OS?thread?handle?0x7f2b58603700,?query?id?1062?192.168.50.57?root?init
/*?ApplicationName=DBeaver?Enterprise?3.8.0?-?Main?*/?show?engine?innodb?status
TABLE?LOCK?table?`lovol_shop`.`T_A`?trx?id?222642?lock?mode?IX
RECORD?LOCKS?space?id?1611?page?no?3?n?bits?72?index?`PRIMARY`?of?table?`lovol_shop`.`T_A`?trx?id?222642?lock_mode?X?locks?rec?but?not?gap
Record?lock,?heap?no?2?PHYSICAL?RECORD:?n_fields?4;?compact?format;?info?bits?0
0:?len?4;?hex?80000001;?asc?????;;
1:?len?6;?hex?0000000365b2;?asc?????e?;;
2:?len?7;?hex?770000025617ba;?asc?w???V??;;
3:?len?1;?hex?31;?asc?1;;
INNODB引擎對于普通的select來說不需要獲取鎖,所以任何語句都不會阻塞普通select的執行。在READ?COMMITTED模式下select?in?update可以成功執行,說明該模式下的select?in?update語句與普通的select一樣不會申請任何鎖。
參考資料:
http://blog.csdn.net/hw_libo/article/details/39080809
http://www.cnblogs.com/zemliu/p/3502395.html
http://blog.csdn.net/xifeijian/article/details/20313977
注:
ANSI?99定義了4種事務隔離級別:
l未提交讀READ?UNCOMMITTED
在讀數據時不會檢查或使用任何鎖。因此,在這種隔離級別中可能讀取到沒有提交的數據。
l已提交讀READ?COMMITTED
只讀取提交的數據并等待其他事務釋放排他鎖。讀數據的共享鎖在讀操作完成后立即釋放。
l可重復讀REPEATABLE?READ
像已提交讀級別那樣讀數據,但會保持共享鎖直到事務結束。
l可序列化SERIALIZABLE
工作方式類似于可重復讀。但它不僅會鎖定受影響的數據,還會鎖定這個范圍。這就阻止了新數據插入查詢所涉及的范圍,這種情況可以導致幻像讀。
Mysql支持全部4種隔離級別,默認級別為REPEATABLE?READ,阿里云RDS默認為READ?COMMITTED。
Oracle僅支持SQL標準中的READ?COMMITTD及SERIALIZABLE,以及特有的READONLY模式。默認級別為READ?COMMITTED。
SqlServer支持4種標準級別,以及兩種使用行版本控制來讀取數據的事務級別(已提交讀快照READ_COMMITTED_SNAPSHOT/快照ALLOW_SNAPSHOT_ISOLATION)。默認級別為READ?COMMITTED.