參考:
https://blog.csdn.net/qq_35590091/article/details/107734005
https://blog.csdn.net/ashic/article/details/53735537
https://blog.csdn.net/QAQ123666/article/details/105084758
MVCC能否解決幻讀?
“幻讀”指,同一個事務里面連續執行兩次同樣的sql語句,可能導致不同結果的問題,第二次sql語句可能會返回之前不存在的行。
先給出結論:不能籠統的說能不能解決,因為有的情況下可以解決,但是有的情況下解決不了。
可以解決的情況
mysql里面實際上有兩種讀,一種是“快照讀”,比如我們使用select進行查詢,就是快照讀,在“快照讀"的情況下是可以解決“幻讀”的問題的。使用的就是MVCC,具體來說如下圖,幾個事務並發執行:
可以看到,盡管別的事務已經提交插入和更新,但是事務A的select讀取的還是一樣的。具體就是mvcc利用歷史版本信息(快照)來控制他能讀取的數據的范圍。具體的可以看看我的關於MVCC淺析的文章。
另外一種讀是:“當前讀”。對於會對數據修改的操作(update、insert、delete)都是采用當前讀的模式,此外,下面兩個語句也是當前讀:
1、select * from table where ? lock in share mode; (加共享鎖)
2、select * from table where ? for update; (加排它鎖)
因此總結一下,下面幾個語句都是當前讀,都會讀取最新的快照數據,都會加鎖(除了第一個加共享鎖,其他都是互斥鎖):
select * from table where ? lock in share mode; select * from table where ? for update; insert; update; delete;
在執行這幾個操作時會讀取最新的記錄,即使是別的事務提交的數據也可以查詢到。比如要update一條記錄,但是在另一個事務中已經delete掉這條數據並且commit了,如果update就會產生沖突,所以在update的時候需要知道最新的數據。讀取的是最新的數據,並且需要加鎖(排它鎖或者共享鎖)。
舉個例子,下面是在可重復讀級別下,事務1在update后,對該數據加鎖,事務B無法插入新的數據,這樣事務A在update前后數據保持一致,避免了幻讀,可以明確的是,update鎖的肯定不只是已查詢到的幾條數據,因為這樣無法阻止insert,有同學會說,那就是鎖住了整張表唄,其實不是,其實這里的鎖,是next-key locking(就是一個行鎖+范圍鎖)實現的.行鎖不必說,就是更新的時候鎖住這一行,這樣別的事務就不能同時進行修改操作了。范圍鎖(gap lock)鎖則是防止插入。
什么是next key lock?
所謂的next key lock就是一個行鎖(record lock)+范圍鎖(gap lock),比如某一個輔助索引(比如上面的class_id),如果它有1,3,5這幾個值,那么當我們使用next key lock的鎖住class_id=1的時候,實際上鎖住了(-無窮,1],或者鎖住class_id=3的時候,實際上鎖住的是(1,3],也就是一個左開右閉的區間。如果此時別的事務要在這個區間內插入數據,就會被阻塞住。這個鎖一直到事務提交才會釋放。因此,即使出現了上面圖片里面這種情況,也可以保證前后兩次去讀的內容一致,因為對這個輔助索引上的鎖是:“next key lock”,他會鎖住一個區間。
但是注意,對於可重復讀默認使用的就是next key lock,但是對於“唯一索引”,比如主鍵的索引,next key lock會降級成行鎖,而不會鎖住一個區間。因此,如果上面的事務1的update使用的是主鍵,事務2也使用主鍵進行插入,那么實際上事務2根本不會被阻塞,可以立即插入並返回。而對於非唯一索引,next key lock則不會降級。
什么情況MVCC也會出現幻讀?
下面這樣的情況:
1.a事務先select,b事務insert確實會加一個gap鎖,但是如果b事務commit,這個gap鎖就會釋放(釋放后a事務可以隨意操作),
2.a事務再select出來的結果在MVCC下還和第一次select一樣,
3.接着a事務不加條件地update,這個update會作用在所有行上(包括b事務新加的),
4.a事務再次select就會出現b事務中的新行,並且這個新行已經被update修改了.
上面這樣,事務2提交之后,事務1再次執行update,因為這個是當前讀,他會讀取最新的數據,包括別的事務已經提交的,所以就會導致此時前后讀取的數據不一致,出現幻讀。
參考:
https://www.cnblogs.com/CoderAyu/p/11525408.html
Mysql(Innodb)如何避免幻讀
幻讀Phantom Rows
The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.
幻讀問題是指一個事務的兩次不同時間的相同查詢返回了不同的的結果集。例如:一個 select 語句執行了兩次,但是在第二次返回了第一次沒有返回的行,那么這些行就是“phantom” row.
read view(或者說 MVCC)實現了一致性不鎖定讀(Consistent Nonlocking Reads),從而避免了(非當前讀下)幻讀
實驗1:
開兩個窗口設置
set session tx_isolation='REPEATABLE-READ';
select @@session.autocommit;select @@global.tx_isolation,@@session.tx_isolation;
create table read_view(text varchar(50));
insert into read_view values('init');
兩個會話開始事務
SESSION_A>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_B>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_A執行一個查詢,這個查詢可以訪問任何表,這個查詢的目的是創建一個當前時間點的快照START TRANSACTION WITH CONSISTENT SNAPSHOT;
也可以達到同樣的效果
SESSION_A>select * from dept;
+--------+------------+----------+
| deptno | dname | loc |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
4 rows in set (0.00 sec)
SESSION_B 插入一條記錄並提交
SESSION_B>insert into read_view values('after session A select');
Query OK, 1 row affected (0.01 sec)
SESSION_B>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A
SESSION_A>select * from read_view;
+------+
| text |
+------+
| init |
+------+
1 row in set (0.00 sec)
SESSION_A>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from read_view;
+------------------------+
| text |
+------------------------+
| init |
| after session A select |
+------------------------+
2 rows in set (0.00 sec)
由於 SESSION_A 第一次的查詢開始於 SESSION_B 插入數據前,所以創建了一個以SELECT操作的時間為基准點的 read view,避免了幻讀的產生
所以在 SESSION_A 的事務結束前,無法看到 SESSION_B 對表 read_view 做出的任何更改 (insert,delete,update)
實驗2
兩個會話開始事務
SESSION_A>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_B>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_B 在 SESSION_A 創建read view 前插入數據
SESSION_B>insert into read_view values('before Session_A select');
Query OK, 1 row affected (0.00 sec)
SESSION_B>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A
SESSION_A>select * from read_view;
+-------------------------+
| text |
+-------------------------+
| init |
| after session A select |
| before Session_A select |
+-------------------------+
3 rows in set (0.00 sec)
SESSION_A>commit
-> ;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from read_view;
+-------------------------+
| text |
+-------------------------+
| init |
| after session A select |
| before Session_A select |
+-------------------------+
3 rows in set (0.00 sec)
由於 SESSION_A 第一次查詢開始於 SESSION_B 對表做出更改並提交后,所以這次的 read view 包含了 SESSION_B 所做出的更改
在官方文檔中寫道
http://dev.mysql.com/doc/refman/5.7/en/innodb-consistent-read.html
A consistent read means that InnoDB uses multi-versioning to present to a query a snapshot of the database at a point in time. The query sees the changes made by transactions that committed before that point of time, and no changes made by later or uncommitted transactions. The exception to this rule is that the query sees the changes made by earlier statements within the same transaction. This exception causes the following anomaly: If you update some rows in a table, a SELECT sees the latest version of the updated rows, but it might also see older versions of any rows. If other sessions simultaneously update the same table, the anomaly means that you might see the table in a state that never existed in the database.
一致性讀是通過 MVCC 為查詢提供了一個基於時間的點的快照。這個查詢只能看到在自己之前提交的數據,而在查詢開始之后提交的數據是不可以看到的。一個特例是,這個查詢可以看到於自己開始之后的同一個事務產生的變化。這個特例會產生一些反常的現象
If the transaction isolation level is REPEATABLE READ (the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction. You can get a fresher snapshot for your queries by committing the current transaction and after that issuing new queries.
在默認隔離級別REPEATABLE READ下,同一事務的所有一致性讀只會讀取第一次查詢時創建的快照
實驗3
兩個會話開始事務
SESSION_A開始事務並創建快照
SESSION_A>START TRANSACTION WITH CONSISTENT SNAPSHOT;
Query OK, 0 rows affected (0.00 sec)
SESSION_B>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from read_view;
+-------------------------+
| text |
+-------------------------+
| init |
| after session A select |
| before Session_A select |
+-------------------------+
3 rows in set (0.00 sec)
SESSION_B>insert into read_view values('anomaly'),('anomaly');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
SESSION_B>update read_view set text='INIT' where text='init';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
SESSION_B>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from read_view;
+-------------------------+
| text |
+-------------------------+
| init |
| after session A select |
| before Session_A select |
+-------------------------+
3 rows in set (0.00 sec)
SESSION_A更新了它並沒有"看"到的行
SESSION_A>update read_view set text='anomaly!' where text='anomaly';
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
SESSION_A>select * from read_view;
+-------------------------+
| text |
+-------------------------+
| init |
| after session A select |
| before Session_A select |
| anomaly! |
| anomaly! |
+-------------------------+
5 rows in set (0.00 sec)
SESSION_A>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from read_view;
+-------------------------+
| text |
+-------------------------+
| INIT |
| after session A select |
| before Session_A select |
| anomaly! |
| anomaly! |
+-------------------------+
5 rows in set (0.00 sec)
觀察實驗步驟可以發現,在倒數第二次查詢中,出現了一個並不存在的狀態
the anomaly means that you might see the table in a state that never existed in the database
這里A的前后兩次讀,均為快照讀,而且是在同一個事務中。但是B先插入直接提交,此時A再update,update屬於當前讀,所以可以作用於新插入的行,並且將修改行的當前版本號設為A的事務號,所以第二次的快照讀,是可以讀取到的,因為同事務號。這種情況符合MVCC的規則,如果要稱為一種幻讀也非不可,算為一個特殊情況來看待吧。
With READ COMMITTED isolation level, each consistent read within a transaction sets and reads its own fresh snapshot.
在 read commit 隔離級別下,同一事務的每個一致性讀sets and reads its own fresh snapshot.
實驗4
修改事務隔離級別set session tx_isolation='READ-COMMITTED'
兩個會話開始事務
SESSION_A>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_B>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from read_view;
+-------------------------+
| text |
+-------------------------+
| INIT |
| after session A select |
| before Session_A select |
| anomaly! |
| anomaly! |
+-------------------------+
5 rows in set (0.00 sec)
SESSION_B>insert into read_view values('hehe');
Query OK, 1 row affected (0.00 sec)
SESSION_B>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from read_view;
+-------------------------+
| text |
+-------------------------+
| INIT |
| after session A select |
| before Session_A select |
| anomaly! |
| anomaly! |
| hehe |
+-------------------------+
6 rows in set (0.00 sec)
read commit 每次讀取都是新的快照
InnoDB通過Nextkey lock解決了當前讀時的幻讀問題
Innodb行鎖分為:
類型 | 說明 |
---|---|
Record Lock: | 在索引上對單行記錄加鎖. |
Gap Lock: | 鎖定一個范圍的記錄,但不包括記錄本身.鎖加在未使用的空閑空間上,可能是兩個索引記錄之間,也可能是第一個索引記錄之前或最后一個索引之后的空間. |
Next-Key Lock: | 行鎖與間隙鎖組合起來用就叫做Next-Key Lock。鎖定一個范圍,並且鎖定記錄本身。對於行的查詢,都是采用該方法,主要目的是解決幻讀的問題。 |
實驗5
創建表
(mysql@localhost) [fandb]> create table t5(id int,key(id));
Query OK, 0 rows affected (0.02 sec)
SESSION_A>insert into t5 values(1),(4),(7),(10);
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
開始實驗
SESSION_A>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from t5;
+------+
| id |
+------+
| 1 |
| 4 |
| 7 |
| 10 |
+------+
4 rows in set (0.00 sec)
SESSION_A>select * from t5 where id=7 for update;
+------+
| id |
+------+
| 7 |
+------+
1 row in set (0.00 sec)
SESSION_B>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_B>insert into t5 values(2);
Query OK, 1 row affected (0.00 sec)
SESSION_B>insert into t5 values(12);
Query OK, 1 row affected (0.00 sec)
SESSION_B>insert into t5 values(5); --被阻塞 ^CCtrl-C -- sending "KILL QUERY 93" to server ...
Ctrl-C -- query aborted.
^[[AERROR 1317 (70100): Query execution was interrupted
SESSION_B>insert into t5 values(7); --被阻塞 ^CCtrl-C -- sending "KILL QUERY 93" to server ...
Ctrl-C -- query aborted.
ERROR 1317 (70100): Query execution was interrupted
SESSION_B>insert into t5 values(9); --被阻塞 ^CCtrl-C -- sending "KILL QUERY 93" to server ...
Ctrl-C -- query aborted.
ERROR 1317 (70100): Query execution was interrupted
SESSION_B>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from t5;
+------+
| id |
+------+
| 1 |
| 4 |
| 7 |
| 10 |
+------+
4 rows in set (0.00 sec)
SESSION_A>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from t5;
+------+
| id |
+------+
| 1 |
| 2 |
| 4 |
| 7 |
| 10 |
| 12 |
+------+
6 rows in set (0.00 sec)
當以當前讀模式select * from t5 where id=7 for update;
獲取 id=7的數據時,產生了 Next-Key Lock,鎖住了4-10范圍和 id=7單個record
從而阻塞了 SESSION_B在這個范圍內插入數據,而在除此之外的范圍內是可以插入數據的。
在倒數第二個查詢中,因為 read view 的存在,避免了我們看到 2和12兩條數據,避免了幻讀
同時因為 Next-Key Lock 的存在,阻塞了其他回話插入數據,因此當前模式讀不會產生幻讀(select for update 是以當前讀模式獲取數據)
###盡量使用唯一索引,因為唯一索引會把Next-Key Lock降級為Record Lock
實驗6
創建表
(mysql@localhost) [fandb]> create table t6(id int primary key);
Query OK, 0 rows affected (0.02 sec)
SESSION_A>insert into t6 values(1),(4),(7),(10);
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
開始實驗
SESSION_A>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from t6;
+----+
| id |
+----+
| 1 |
| 4 |
| 7 |
| 10 |
+----+
4 rows in set (0.00 sec)
SESSION_A>select * from t6 where id=7 for update; +----+
| id |
+----+
| 7 |
+----+
1 row in set (0.00 sec)
SESSION_B>begin;
Query OK, 0 rows affected (0.00 sec)
SESSION_B>insert into t6 values(5); --插入成功沒有阻塞 Query OK, 1 row affected (0.00 sec)
SESSION_B>insert into t6 values(8); --插入成功沒有阻塞 Query OK, 1 row affected (0.00 sec)
SESSION_B>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from t6;
+----+
| id |
+----+
| 1 |
| 4 |
| 7 |
| 10 |
+----+
4 rows in set (0.00 sec)
SESSION_A>commit;
Query OK, 0 rows affected (0.00 sec)
SESSION_A>select * from t6;
+----+
| id |
+----+
| 1 |
| 4 |
| 5 |
| 7 |
| 8 |
| 10 |
+----+
6 rows in set (0.00 sec)
當 id 列有唯一索引,Next-Key Lock 會降級為 Records Lock
MySQL解決幻讀——MVCC與間隙鎖
當前讀 與 快照讀
當前讀:
select...lock in share mode (共享讀鎖)
select...for update
update , delete , insert
當前讀, 讀取的是最新版本, 並且對讀取的記錄加鎖, 阻塞其他事務同時改動相同記錄,避免出現安全問題。
例如,假設要update一條記錄,但是另一個事務已經delete這條數據並且commit了,如果不加鎖就會產生沖突。所以update的時候肯定要是當前讀,得到最新的信息並且鎖定相應的記錄。
快照讀:
簡單的select操作(不包括 select … lock in share mode, select … for update)。
Read Committed隔離級別:每次select都生成一個快照讀。
Read Repeatable隔離級別:開啟事務后第一個select語句才是快照讀的地方,而不是一開啟事務就快照讀。
在RR級別下,快照讀是通過MVVC(多版本控制)和undo log來實現的,當前讀是通過加record lock(記錄鎖)和gap lock(間隙鎖)來實現的。
如何解決幻讀
快照讀的幻讀是用MVCC解決的,當前的讀的幻讀是用間隙鎖解決的。
innodb的默認事務隔離級別是rr(可重復讀)。它的實現技術是mvcc。該技術不僅可以保證innodb的可重復讀,而且可以防止幻讀。(這也就是是此前以rr隔離級別實踐時,不僅可以防止可重復讀,也防止了幻讀)但是它防止的是快照讀,也就是讀取的數據雖然是一致的,但是數據是歷史數據。
這個帖子里面就有一個實例:MySQL的InnoDB的幻讀問題
一些文章寫到InnoDB的可重復讀避免了“幻讀”(phantom read),這個說法並不准確。
那InnoDB指出的可以避免幻讀是怎么回事呢?
以下翻譯自MySQL官網文檔(http://download.nust.na/pub6/mysql/doc/refman/5.5/en/innodb-next-key-locking.html),翻譯水平一般,請見諒。
當隔離級別是可重復讀,且禁用innodb_locks_unsafe_for_binlog的情況下,在搜索和掃描index的時候使用的next-key locks可以避免幻讀。也就是說的間隙鎖。
InnoDB提供了next-key locks,但需要應用程序自己去加鎖
舉個例子:
SELECT * FROM child WHERE id> 100 FOR UPDATE;
該查詢從id大於100 的第一條記錄開始掃描索引 。如果該表包含具有id在90和102值的行。如果在掃描范圍內的索引記錄上設置的鎖沒有鎖定在間隙中插入的內容(在這種情況下,在介於90和102之間,則另一個事務可以在表中插入一個新行,其行號id為101。如果您要在同一個事務中執行相同的SELECT,您將在查詢返回的結果集中看到一個id為101的新行(一個“幻影”),這就產生了幻讀。這與事務的隔離原則是相反的:一個事務應該能夠運行,以便它已經讀的數據在事務過程中不改變。如果我們把一套行視為數據項,新的“幽靈”子記錄可能會違反這一隔離原則。
為了防止“幻影”,InnoDB使用了一種名為next-key locking
的算法,它將 索引行鎖定 和 間隙鎖定 結合在一起。InnoDB執行行級鎖的方式是,當它搜索或掃描一個表索引時,它會在遇到的索引記錄上設置共享或獨占鎖。因此,行級鎖實際上是索引記錄鎖。此外,索引記錄上的next-key lock 也會影響該索引記錄之前的“間隙”。也就是說,next-key lock 是一個index-record lock(索引記錄鎖)加上一個在索引記錄之前的間隙上的 gap lock (間隙鎖)。如果一個會話在索引中的記錄R上具有共享鎖或獨占鎖,則另一個會話不能在R之前的間隙中插入新的索引記錄。
當InnoDB掃描一個索引之時,它也鎖定所有記錄中最后一個記錄之后的間隙。剛在前一個例子中發生:InnoDB設置的鎖定防止任何插入到id可能大過100的表。所有插入id為101的數據是無法執行的
您可以使用 next-key lock在應用程序中實現唯一性檢查:如果你讀數據時加了共享鎖(select … from lock in share mode; 生成表級共享鎖,允許其它線程讀取數據但不能修改數據。)和沒有看到你要復制的行插入,那么您可以安全地插入行並且明白:在讀取期間,對您的行的后續行設置的next-key鎖將防止任何人同時為您的行插入副本。因此,next-key鎖允許您“鎖定”表中不存在的內容。
假設我們有一個表:city,結構如下:
在第一個查詢窗口中開始一個事務:
事務A | 事務B |
---|---|
BEGIN; | |
SELECT * FROM city WHERE id > 2 |
|
BEGIN; | |
INSERT INTO city VALUES (6, ‘成都’); | |
COMMIT; | |
SELECT * FROM city WHERE id > 2 |
|
SELECT * FROM city WHERE id > 2 LOCK IN SHARE MODE |
問,事務A的三個select分別是什么結果:
第一個:南京,廣州,杭州
第二個:南京,廣州,杭州
第三個:南京,廣州,杭州,成都
我們可以看出,如果使用普通的讀,會得到一致性的結果,如果使用了加鎖的讀,就會讀到“最新的”“提交”讀的結果。如果需要實時顯示數據,還是需要通過手動加鎖來實現。這個時候會使用next-key技術來實現。
本身,可重復讀和提交讀是矛盾的。在同一個事務里,如果保證了可重復讀,就會看不到其他事務的提交,違背了提交讀;如果保證了提交讀,就會導致前后兩次讀到的結果不一致,違背了可重復讀。InnoDB提供了這樣的機制,在默認的可重復讀的隔離級別里,可以使用加鎖讀去查詢最新的數據: IN SHARE MODE;
我們再開啟一個事務,執行:
BEGIN; INSERT INTO city VALUES (7, '濟南'); COMMIT;
會怎樣?
結果是阻塞。因為加了間隙鎖
我們再開啟一個事務,執行:
BEGIN; UPDATE city SET name = '濟南' WHERE ID = 1 UPDATE city SET name = '濟南' WHERE ID = 2 COMMIT;
會怎樣?
修改成功,沒問題,間隙鎖沒加到這。
如果我寫
BEGIN; UPDATE city SET name = '濟南' WHERE ID = 1
肯定會阻塞,因為間隙鎖的原因。
結論:MySQL InnoDB的可重復讀並不保證避免幻讀,需要應用使用加鎖讀來保證。而這個加鎖度使用到的機制就是next-key locks。
在mysql中,提供了兩種事務隔離技術,第一個是mvcc,第二個是next-key技術。這個在使用不同的語句的時候可以動態選擇。不加lock inshare mode之類的快照讀就使用mvcc。否則 當前讀使用next-key。mvcc的優勢是不加鎖,並發性高。缺點是不是實時數據。next-key的優勢是獲取實時數據,但是需要加鎖。
另外,重要:
在rr級別下,mvcc完全解決了重復讀,但並不能真正的完全避免幻讀,只是在部分場景下利用歷史數據規避了幻讀
對於快照讀,mysql使用mvcc利用歷史數據部分避免了幻讀(在某些場景看上去規避了幻讀)
要完全避免,需要手動加鎖將快照讀調整為當前讀(mysql不會自動加鎖),然后mysql使用next-key完全避免了幻讀,比如rr下,鎖1(0,2,3,4),另一個線程的insert 3即被阻塞,在rc下,另一個線程仍然可以大搖大擺的插入,如本線程再次查詢比如count,則會不一致
建議去看官方文檔。
參考文章:
innodb當前讀 與 快照讀
MySQL的InnoDB的幻讀問題
相關的官方文檔
MySQL的鎖機制 - 記錄鎖、間隙鎖、臨鍵鎖
【MySQL】當前讀、快照讀、MVCC