mysql innodb默認事務隔離級別--repeatable read對幻讀的影響實驗
測試準備:
1.show variables like ‘%unsafe%‘;確保 innodb_locks_unsafe_for_binlog值為 OFF(或者0);
2.show variables like ‘%tx%‘; 確保 tx_isolation 的值為 REPEATABLE-READ。
3.新建表t_test :
CREATE TABLE t_test ( id INT(32) NOT NULL, val CHAR(1) DEFAULT ‘0‘ NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
test1:
start transaction; select * from t_test where id >100; //無結果 select sleep(10); // >>此時執行tx2: insert into t_test values (102,0); //插入成功 select * from t_test where id >100; //無結果 commit;
結論:第二次查詢與第一次一致,沒有出現幻讀。根據mysql repeatable read的mvcc機制,兩次select讀均為快照讀(即讀取的數據來自undo log)。這也是避免了不可重復讀的原因。
test2:
start transaction; select * from t_test where id >100; //無結果 select sleep(10); // >>此時執行tx2: insert into t_test values (103,0); //插入成功 update t_test set val=1 where id >100; select * from t_test where id >100; //查詢到了103這一條 commit;
結論:第二次查詢出現幻讀。執行update時刷新了undo log中涉及到的行的信息。
test3:
start transaction; select * from t_test where id >100;//有id為103這一行原始數據 select sleep(10); //>>此時執行tx2:insert into t_test values (104,0); //插入成功 update t_test set val=2 where id <104; select * from t_test where id >100;//只查詢到id為103的行 commit;
結論:未出現幻讀。和test2相比,update的where條件沒有覆蓋到tx2的insert行。
test4:
start transaction; select * from t_test where id >104 FOR UPDATE; //無結果 select sleep(10); // >>此時執行tx2:insert into t_test values (105,0); //阻塞 直到tx1 commit後插入成功 update t_test set val=1 where id >100; select * from t_test where id >104; //無結果 commit;結論:未出現幻讀。第一個查詢的 FOR UPDATE相當於給id>100的數據加了next-key lock 鎖。當然,此處用共享鎖LOCK IN SHARE MODE結果是一樣的。
test5:
start transaction; select * from t_test where id >105; //無結果 select sleep(10); // >>此時執行tx2:insert into t_test values (106,0); //插入成功 select * from t_test where id >105 FOR UPDATE; //出現幻讀 commit;
結論:第二次查詢出現幻讀。第二次加鎖的查詢為當前讀(即跳過undo log,直接查庫)。
test6:
start transaction; select * from t_test where id =106; //id:106 val:0 select sleep(10); // >>此時執行tx2:update t_test set val =‘8‘ where id=106;//執行成功 select * from t_test where id =106 FOR UPDATE; //id:106 val:8 出現不可重復讀 commit;
結論:第二次查詢出現不可重復讀。原理同test5
test7:
start transaction; select * from t_test where id =107;//無結果 select sleep(10); //>>此時執行insert into t_test values (110,0); //插入成功 select * from t_test where id =107 FOR UPDATE; //查詢到id為110的數據 select * from t_test where id =107;//無結果 commit;
結論:第三次查詢沒有受到第二次加鎖查詢的影響,說明加鎖查詢並不會改變undo log的信息
總結:我們在這裏可以將undo log抽象地理解為一種緩存機制 ,我們只要保證這個“緩存”在事務開始時是最新數據的緩存,並記錄該緩存版本號,即使在事務執行過程中有其他事務提交數據的增刪改操作,也不會影響到該版本號“緩存”的數據。在事務中執行不加鎖的select操作時,會直接從“緩存”中拿數據;在事務中執行增刪改時,會更新undo log中影響的數據行的信息,而若在第一次查詢和更新之間的時間範圍內,有其他事務提交了插入數據的操作,且更新時的條件覆蓋了插入的數據,則會導致幻讀;而對查詢語句加鎖,則會直接從庫中查數據,也就相當於使用了“READ COMMITTED”的隔離級別,但這種操作並不會改變undo log。
mysql innodb默認事務隔離級別--repeatable read對幻讀的影響實驗