Mysql事務隔離級別對select in update的影響(InnoDB)

本文探討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.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容