在使用MySQL數據庫時,有時會出現ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 這樣的報錯。而在一個事務中,如果其中一條sql執行時出現此報錯,對本事務的其他腳本是否有影響呢,后面如果執行commit操作,報錯之前語句的結果是否成功呢? 這個結果與隔離級別以及innodb_rollback_on_timeout參數設置有關。
注: MySQL默認隔離級別為 REPEATABLE-READ,innodb_rollback_on_timeout為OFF,本文基於innodb表(支持事務)進行測試。
1. 准備工作
1.1 測試環境
MySQL 8.0
1.2 創建測試表及預備數據
創建一張測試表,並插入一條記錄
mysql> use testdb; Database changed mysql> create table test1(id int primary key,name varchar(20)); Query OK, 0 rows affected (0.01 sec) mysql> insert into test1 values(1,'1wdrt5'); Query OK, 1 row affected (0.00 sec) mysql> select * from test1; +----+--------+ | id | name | +----+--------+ | 1 | 1wdrt5 | +----+--------+ 1 row in set (0.00 sec)
下面將根據不同的隔離級別及innodb_rollback_on_timeout啟停情況進行測試。
2. 測試過程
2.1 隔離級別REPEATABLE-READ & innodb_rollback_on_timeout =OFF
a) 測試過程:
session A | session B |
mysql> begin;
mysql> select * from test1; |
mysql> begin; mysql> select * from test1;
|
b) 測試結果:
隔離級別REPEATABLE-READ & innodb_rollback_on_timeout =OFF (2個參數均為默認值)的情況下,即使事務中有超時回滾報錯,超時前的sql不會回滾,依舊執行成功。
2.2 隔離級別為READ-COMMITTED & innodb_rollback_on_timeout =OFF
a) 測試過程
session A | session B |
mysql> show global variables like 'transaction_isolation';
mysql> commit; mysql> select * from test1; |
mysql> begin;
|
b)測試結果:
隔離級別為READ-COMMITTED & innodb_rollback_on_timeout =OFF 情況下,即使事務中有超時回滾報錯,超時前的sql不會回滾,依舊執行成功,同2者均為默認值的情況。
2.3 隔離級別REPEATABLE-READ & innodb_rollback_on_timeout =ON
注: innodb_rollback_on_timeout不能在線修改,需要修改配置文件后重啟生效
測試過程:
a) 修改配置文件,重啟數據庫
在my.cnf文件里添加innodb_rollback_on_timeout=on 再重啟數據庫即可生效
mysql> show global variables like 'transaction_isolation'; +-----------------------+-----------------+ | Variable_name | Value | +-----------------------+-----------------+ | transaction_isolation | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set (0.00 sec) mysql> show global variables like 'innodb_rollback_on_timeout'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | innodb_rollback_on_timeout | ON | +----------------------------+-------+ 1 row in set (0.00 sec)
b) 事務測試過程
session A | session B |
mysql> select * from test1;
mysql> commit; |
mysql> use testdb; |
c) 測試結果:
隔離級別REPEATABLE-READ & innodb_rollback_on_timeout =ON 的情況下,事務中有超時回滾報錯時,超時前sql也會回滾。
2.4 隔離級別為READ-COMMITTED & innodb_rollback_on_timeout =ON
a) 參數調整
mysql> set global transaction_isolation='READ-COMMITTED'; mysql> exit # 重新登錄 mysql> show global variables like 'transaction_isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | READ-COMMITTED | +-----------------------+----------------+ 1 row in set (0.00 sec) mysql> show global variables like 'innodb_rollback_on_timeout'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | innodb_rollback_on_timeout | ON | +----------------------------+-------+ 1 row in set (0.00 sec)
b) 測試過程
session A | session B |
mysql> use testdb;
mysql> commit; |
mysql> use testdb; |
c) 測試結果
隔離級別為READ-COMMITTED & innodb_rollback_on_timeout =ON的情況下,即使事務中有超時回滾報錯,超時前的sql不會回滾,依舊執行成功,同2者均為默認值的情況。
3. 小結
在MySQL8.0 中,僅有在隔離級別為READ-COMMITTED & innodb_rollback_on_timeout =ON情況下,事務中有超時回滾報錯時,超時前sql也會回滾。
隔離級別 | innodb_rollback_on_timeout | 結果 |
REPEATABLE-READ | OFF | 超時回滾前的SQL不會自動回滾 |
READ-COMMITTED | OFF | 超時回滾前的SQL不會自動回滾 |
REPEATABLE-READ | ON | 超時回滾前的SQL會自動回滾 |
READ-COMMITTED | ON | 超時回滾前的SQL不會自動回滾 |
TIPS:
1) 測試過程中可以查看information_schema.innodb_trx表觀察事務情況,在不同的版本中事務情況不一樣.例如,隔離級別REPEATABLE-READ & innodb_rollback_on_timeout=on的情況下,MySQL5.6 中整個事務回滾后會自動創建一個事務,而MySQL5.7則不會再自動創建事務。
2) 在生產環境使用中,建議將innodb_rollback_on_timeout 設置為ON。應用程序一定要做好事務控制,在一個事務出現異常時必須進行顯式rollback