1. 程式人生 > >mysql innodb默認事務隔離級別--repeatable read對幻讀的影響實驗

mysql innodb默認事務隔離級別--repeatable read對幻讀的影響實驗

也不會 fault 實驗 undo log 提交數據 spl primary 一個 ron

測試準備:

  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對幻讀的影響實驗