因爲不少人(固然也包括本人), 容易搞混 不可重複讀
和 幻讀
, 這二者確實很是類似。html
不可重複讀
主要是說屢次讀取一條記錄, 發現該記錄中某些列值被修改過。幻讀
主要是說屢次讀取一個範圍內的記錄(包括直接查詢全部記錄結果或者作聚合統計), 發現結果不一致(標準檔案通常指記錄增多, 記錄的減小應該也算是幻讀)。(能夠參考MySQL官方文檔對 Phantom Rows 的介紹)幻讀
, MySQL的InnoDB引擎默認的RR
級別已經經過MVCC自動幫咱們解決了
, 因此該級別下, 你也模擬不出幻讀的場景; 退回到 RC
隔離級別的話, 你又容易把幻讀
和不可重複讀
搞混淆, 因此這可能就是比較頭痛的點吧!RR
隔離級別的描述, 理論上RR級別是沒法解決幻讀的問題, 可是因爲InnoDB引擎的RR級別還使用了MVCC, 因此也就避免了幻讀的出現!MVCC雖然解決了幻讀
問題, 但嚴格來講只是解決了部分幻讀問題, 接下來進行演示:mysql
1.打開客戶端1查看隔離級別及初始數據sql
mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | REPEATABLE-READ | +------------------------+ 1 row in set (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 1 | 金剛狼 | 127 | 1 | 我有一雙鐵爪 | | 2 | 鋼鐵俠 | 120 | 1 | 我有一身鐵甲 | | 3 | 綠巨人 | 0 | 2 | 我有一身肉 | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql>
2.打開客戶端2查看隔離級別及初始數據數據庫
mysql> SELECT @@SESSION.tx_isolation; +------------------------+ | @@SESSION.tx_isolation | +------------------------+ | REPEATABLE-READ | +------------------------+ 1 row in set (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 1 | 金剛狼 | 127 | 1 | 我有一雙鐵爪 | | 2 | 鋼鐵俠 | 120 | 1 | 我有一身鐵甲 | | 3 | 綠巨人 | 0 | 2 | 我有一身肉 | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql>
3.在客戶端2中開啓事務, 而後查詢數據session
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 1 | 金剛狼 | 127 | 1 | 我有一雙鐵爪 | | 2 | 鋼鐵俠 | 120 | 1 | 我有一身鐵甲 | | 3 | 綠巨人 | 0 | 2 | 我有一身肉 | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql>
4.在客戶端1中插入一條id爲4的新數據 (直接自動提交)併發
mysql> insert into test_transaction (`id`,`user_name`,`age`,`gender`,`desctiption`) values (4, '死侍', 18, 0, 'A bad boy'); Query OK, 1 row affected (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 1 | 金剛狼 | 127 | 1 | 我有一雙鐵爪 | | 2 | 鋼鐵俠 | 120 | 1 | 我有一身鐵甲 | | 3 | 綠巨人 | 0 | 2 | 我有一身肉 | | 4 | 死侍 | 18 | 0 | A bad boy | +----+-----------+-----+--------+--------------------+ 4 rows in set (0.00 sec) mysql>
5.在客戶端2事務中再次查詢數據, 發現數據沒有變化(表示能夠重複讀, 而且克服了幻讀)!! 可是在客戶端2事務中插入一條id爲4的新數據, 發現提示數據已經存在!!!app
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 1 | 金剛狼 | 127 | 1 | 我有一雙鐵爪 | | 2 | 鋼鐵俠 | 120 | 1 | 我有一身鐵甲 | | 3 | 綠巨人 | 0 | 2 | 我有一身肉 | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql> select * from test_transaction; +----+-----------+-----+--------+--------------------+ | id | user_name | age | gender | desctiption | +----+-----------+-----+--------+--------------------+ | 1 | 金剛狼 | 127 | 1 | 我有一雙鐵爪 | | 2 | 鋼鐵俠 | 120 | 1 | 我有一身鐵甲 | | 3 | 綠巨人 | 0 | 2 | 我有一身肉 | +----+-----------+-----+--------+--------------------+ 3 rows in set (0.00 sec) mysql> insert into test_transaction (`id`,`user_name`,`age`,`gender`,`desctiption`) values (4, '死侍', 18, 0, 'A bad boy'); 1062 - Duplicate entry '4' for key 'PRIMARY' mysql> //而且, 此時`update/delete`也是能夠操做這條在事務中看不到的記錄的!
6.那麼這是什麼問題呢?性能
The snapshot of the database state applies to SELECT statements within a transaction, not necessarily to DML statements. If you insert or modify some rows and then commit that transaction, a DELETE or UPDATE statement issued from another concurrent REPEATABLE READ transaction could affect those just-committed rows, even though the session could not query them. If a transaction does update or delete rows committed by a different transaction, those changes do become visible to the current transaction.
我的認爲應該翻譯爲: 數據庫狀態的快照適用於事務中的SELECT語句, 而不必定適用於全部DML語句。 若是您插入或修改某些行, 而後提交該事務, 則從另外一個併發REPEATABLE READ事務發出的DELETE或UPDATE語句就可能會影響那些剛剛提交的行, 即便該事務沒法查詢它們。 若是事務更新或刪除由不一樣事務提交的行, 則這些更改對當前事務變得可見。
7.很多資料將MVCC併發控制中的讀操做能夠分紅兩類: 快照讀 (snapshot read)
與 當前讀 (current read)
。翻譯
- 快照讀, 讀取專門的快照 (對於RC,快照(ReadView)會在每一個語句中建立。對於RR,快照是在事務啓動時建立的) ``` 簡單的select操做便可(不須要加鎖,如: select ... lock in share mode, select ... for update) ``` 針對的也是select操做 - 當前讀, 讀取最新版本的記錄, 沒有快照。 在InnoDB中,當前讀取根本不會建立任何快照。 ``` select ... lock in share mode select ... for update ``` 針對以下操做, 會讓以下操做阻塞: ``` insert update delete ``` - 在RR級別下, 快照讀是經過MVVC(多版本控制)和undo log來實現的, 當前讀是經過手動加record lock(記錄鎖)和gap lock(間隙鎖)來實現的。因此從上面的顯示來看,若是須要實時顯示數據,仍是須要經過加鎖來實現。這個時候會使用next-key技術來實現。
8.固然, 使用隔離性
的最高隔離級別SERIALIZABLE
也能夠解決幻讀
, 但該隔離級別在實際中不多使用!版本控制