幻讀是一種在并發(fā)場(chǎng)景下的數(shù)據(jù)不一致問(wèn)題,比如多個(gè)事務(wù)對(duì)同一個(gè)表進(jìn)行查詢(xún),其中一個(gè)事務(wù)對(duì)結(jié)果集進(jìn)行了修改,這時(shí)候另一個(gè)事務(wù)再查詢(xún)時(shí)發(fā)現(xiàn)結(jié)果集并不符合預(yù)期,就稱(chēng)為幻讀。
MySQL為了避免幻讀這類(lèi)問(wèn)題,使用了多版本控制(MVCC)機(jī)制。MVCC機(jī)制是通過(guò)創(chuàng)建快照來(lái)實(shí)現(xiàn)的,每個(gè)事務(wù)在訪問(wèn)表時(shí)都有其自己的快照。
在MVCC機(jī)制下,每個(gè)行都有一個(gè)版本號(hào),事務(wù)讀取行時(shí),只能讀取版本號(hào)小于它自己的行。這樣就可以防止事務(wù)讀到其他事務(wù)正在修改的行。同時(shí),修改操作會(huì)給行打上新的版本號(hào),保證其他事務(wù)讀到的是老版本,從而避免幻讀問(wèn)題。
-- 創(chuàng)建一個(gè)測(cè)試表 CREATE TABLE test_table ( id INT PRIMARY KEY, name VARCHAR(20) NOT NULL ); -- 設(shè)置事務(wù)隔離級(jí)別 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 事務(wù)1插入一行 START TRANSACTION; INSERT INTO test_table (id, name) VALUES (1, 'test1'); COMMIT; -- 事務(wù)2查詢(xún)表中所有行,此時(shí)只有一行,id為1 START TRANSACTION; SELECT * FROM test_table; -- 結(jié)果集中有1行 -- 事務(wù)1插入一行,此時(shí)表中有2行 START TRANSACTION; INSERT INTO test_table (id, name) VALUES (2, 'test2'); COMMIT; -- 事務(wù)2再次查詢(xún)表中所有行,此時(shí)有2行,id為1和2 START TRANSACTION; SELECT * FROM test_table; -- 結(jié)果集中有2行,id為1和2
通過(guò)上述示例可以看到,使用了REPEATABLE READ隔離級(jí)別后,事務(wù)2在第二次查詢(xún)時(shí)只能看到新插入的那一行,符合預(yù)期,避免了幻讀問(wèn)題。