1. 程式人生 > >mysql的可重複讀REPEATABLE READ隔離級別和幻讀

mysql的可重複讀REPEATABLE READ隔離級別和幻讀

1)mvcc多版本控制提高讀寫qps

2) REPEATBLE READ 級別並不能完全避免幻讀,需要加next key locks,可以使顯示鎖(select * where * for update   or lock in share mode)

一些文章寫到InnoDB的可重複讀避免了“幻讀”(phantom read),這個說法並不準確。

做個試驗:(以下所有試驗要注意儲存引擎和隔離級別)

mysql>show create table t_bitfly/G;
CREATE TABLE `t_bitfly` (
`id` bigint(20) NOT NULL default '0',
`value` varchar(32) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk

mysql>select @@global.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@global.tx_isolation | @@tx_isolation  |
+-----------------------+-----------------+
| REPEATABLE-READ       | REPEATABLE-READ |
+-----------------------+-----------------+

試驗4-1:

tSessionA                                                     Session B
|
| START TRANSACTION;                           START TRANSACTION;
|
| SELECT * FROM t_bitfly;
| empty set
|                                INSERT INTO t_bitfly VALUES (1, 'a');
|                             
|
| SELECT * FROM t_bitfly;
| empty set
|                                                                      COMMIT;
|
| SELECT * FROM t_bitfly;
| empty set
|
| INSERT INTO t_bitfly VALUES (1, 'a');
| ERROR 1062 (23000):
| Duplicate entry '1' for key 1
v (shit,剛剛明明告訴我沒有這條記錄的)

如此就出現了幻讀,以為表裡沒有資料,其實資料已經存在了,傻乎乎的提交後,才發現數據衝突了。

試驗4-2:

tSessionA               Session B
|
| START TRANSACTION;           START TRANSACTION;
|
| SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+
|                                       INSERT INTO t_bitfly VALUES (2, 'b');
|                            
|
| SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+
|                               COMMIT;
|
| SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+
|
| UPDATE t_bitfly SET value='z';
| Rows matched: 2  Changed:2  Warnings: 0
| (怎麼多出來一行)
|
| SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |z     |
| |    2 |z     |
| +------+-------+
|
v

本事務中第一次讀取出一行,做了一次更新後,另一個事務裡提交的資料就出現了。也可以看做是一種幻讀。

------

那麼,InnoDB指出的可以避免幻讀是怎麼回事呢?

http://dev.mysql.com/doc/refman/5.0/en/innodb-record-level-locks.html

By default, InnoDB operatesin REPEATABLE READ transaction isolation level and with the innodb_locks_unsafe_for_binlogsystem variable disabled. In this case, InnoDB uses next-key locks for searchesand index scans, which prevents phantom rows (see Section 13.6.8.5, “Avoidingthe Phantom Problem Using Next-Key Locking”).

準備的理解是,當隔離級別是可重複讀,且禁用innodb_locks_unsafe_for_binlog的情況下,在搜尋和掃描index的時候使用的next-keylocks可以避免幻讀。

關鍵點在於,是InnoDB預設對一個普通的查詢也會加next-key locks,還是說需要應用自己來加鎖呢?如果單看這一句,可能會以為InnoDB對普通的查詢也加了鎖,如果是,那和序列化(SERIALIZABLE)的區別又在哪裡呢?

MySQL manual裡還有一段:

13.2.8.5. Avoiding the PhantomProblem Using Next-Key Locking (http://dev.mysql.com/doc/refman/5.0/en/innodb-next-key-locking.html)

Toprevent phantoms, InnoDB usesan algorithm called next-key locking that combinesindex-row locking with gap locking.

Youcan use next-key locking to implement a uniqueness check in your application:If you read your data in share mode and do not see a duplicate for a row youare going to insert, then you can safely insert your row and know that thenext-key lock set on the successor of your row during the read prevents anyonemeanwhile inserting a duplicate for your row. Thus, the next-key lockingenables you to “lock” the nonexistence of something in your table.

我的理解是說,InnoDB提供了next-key locks,但需要應用程式自己去加鎖。manual裡提供一個例子:

SELECT * FROM child WHERE id> 100 FOR UPDATE;

這樣,InnoDB會給id大於100的行(假如child表裡有一行id為102),以及100-102,102+的gap都加上鎖。

可以使用showinnodb status來檢視是否給表加上了鎖。

再看一個實驗,要注意,表t_bitfly裡的id為主鍵欄位。

實驗4-3:

t SessionA             Session B
|
| START TRANSACTION;                START TRANSACTION;
|
| SELECT * FROM t_bitfly
| WHERE id<=1
| FOR UPDATE;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+
|                                      INSERT INTO t_bitfly   VALUES (2, 'b');
|                            Query OK, 1 row affected
|
| SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+
|                              INSERT INTO t_bitfly
|                              VALUES (0, '0');
|                                  (waiting for lock ...
|                               then timeout)
|                                   ERROR 1205 (HY000):
|                                  Lock wait timeout exceeded;
|                                   try restarting transaction
|
| SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+
|                           COMMIT;
|
| SELECT * FROM t_bitfly;
| +------+-------+
| | id   | value |
| +------+-------+
| |    1 |a     |
| +------+-------+
v

可以看到,用id<=1加的鎖,只鎖住了id<=1的範圍,可以成功新增id為2的記錄,新增id為0的記錄時就會等待鎖的釋放。

MySQL manual裡對可重複讀裡的鎖的詳細解釋:

http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read

Forlocking reads (SELECT with FORUPDATE or LOCK IN SHARE MODE),UPDATE, and DELETE statements, lockingdepends on whether the statement uses a unique index with a unique searchcondition, or a range-type search condition. For a unique index with a uniquesearch condition, InnoDB locksonly the index record found, not the gap before it. For other searchconditions, InnoDB locksthe index range scanned, using gap locks or next-key (gap plus index-record)locks to block insertions by other sessions into the gaps covered by the range.

------

一致性讀和提交讀,先看實驗,

實驗4-4:

tSessionA              Session B
|
| STARTTRANSACTION;             START TRANSACTION;
|
| SELECT * FROM t_bitfly;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 |a     |
| +----+-------+
|                              INSERT INTO t_bitfly   VALUES (2, 'b');
|                              
|                              COMMIT;
|
| SELECT * FROM t_bitfly;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 |a     |
| +----+-------+
|
| SELECT * FROM t_bitfly LOCK IN SHARE MODE;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 |a     |
| |  2 |b     |
| +----+-------+
|
| SELECT * FROM t_bitfly FOR UPDATE;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 |a     |
| |  2 |b     |
| +----+-------+
|
| SELECT * FROM t_bitfly;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 |a     |
| +----+-------+
v

如果使用普通的讀,會得到一致性的結果,如果使用了加鎖的讀,就會讀到“最新的”“提交”讀的結果。

本身,可重複讀和提交讀是矛盾的。在同一個事務裡,如果保證了可重複讀,就會看不到其他事務的提交,違背了提交讀;如果保證了提交讀,就會導致前後兩次讀到的結果不一致,違背了可重複讀。

可以這麼講,InnoDB提供了這樣的機制,在預設的可重複讀的隔離級別裡,可以使用加鎖讀去查詢最新的資料。

http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html

Ifyou want to see the “freshest” state of the database, you should use either theREAD COMMITTED isolation level or a locking read:
SELECT * FROM t_bitfly LOCK IN SHARE MODE;

------

結論:MySQLInnoDB的可重複讀並不保證避免幻讀,需要應用使用加鎖讀來保證。而這個加鎖度使用到的機制就是next-keylocks。


相關推薦

mysql重複REPEATABLE READ隔離級別

1)mvcc多版本控制提高讀寫qps 2) REPEATBLE READ 級別並不能完全避免幻讀,需要加next key locks,可以使顯示鎖(select * where * for update   or lock in share mode) 一些文章寫到I

淺析MySQL資料庫在InnoDB儲存引擎下的READ COMMITTED與REPEATABLE READ隔離級別以及不可重複現象

一、InnoDB儲存引擎下的一致性非鎖定讀與一致性鎖定讀 MySQL在InnoDB引擎下對於READ COMMITTED與REPEATABLE READ這兩個隔離級別均採用的是一致性非鎖定讀,即所謂的多版本併發控制協議(MVCC),這樣增加了併發效能。這兩種隔離級別下預設的讀操作都是讀快照,但是

MySQL--REPEATABLE-READ隔離級別下讀取到的“重復數據”

屬於 兩個 客戶端 並發控制 存在 讀取 select 執行 create 在MySQL中,使用MVCC來實現REPEATABLE-READ隔離級別,由於SELECT操作不會對數據加鎖,其他回話可以修改當前回話所讀取過的數據而不會被阻塞,因此讀寫不沖突。 在MVCC並發控

MYSQL REPEATABLE-READ隔離級別

REPEATABLE-READ 即可重複讀,set autocommit= 0或者START TRANSACTION狀態下select表的內容不會改變。這種隔離級別可能導致讀到的東西是已經修改過的。 比如: 回話一中讀取一個欄位一行a=1  在回話二里這個欄位該行修改a=0

MySQL的InnoDB預設隔離級別問題

MySQL InnoDB事務的隔離級別有四級,預設是“可重複讀”(REPEATABLE READ)。 未提交讀(READ UNCOMMITTED)。另一個事務修改了資料,但尚未提交,而本事務中的SELECT會讀到這些未被提交的資料(髒讀)。 提交讀(READ COMMITTED)。本事務讀取到的是

MySQL使用重複作為預設隔離級別的原因(二)-》Innodb鎖機制:Next-Key Lock 淺談

資料庫使用鎖是為了支援更好的併發,提供資料的完整性和一致性。InnoDB是一個支援行鎖的儲存引擎,鎖的型別有:共享鎖(S)、排他鎖(X)、意向共享(IS)、意向排他(IX)。為了提供更好的併發,InnoDB提供了非鎖定讀:不需要等待訪問行上的鎖釋放,讀取行的一個快照。該方法是通過InnoDB的一個特

MySQL使用重複作為預設隔離級別的原因(一)

一般的DBMS系統,預設都會使用讀提交(Read-Comitted,RC)作為預設隔離級別,如Oracle、SQL Server等,而MySQL卻使用可重複讀(Read-Repeatable,RR)。要知道,越高的隔離級別,能解決的資料一致性問題越多,理論上效能損耗更大,可併發性越低。隔離

MySQL使用重複作為預設隔離級別的原因

本文轉載於  http://www.linuxidc.com/Linux/2017-02/140847.htm 一般的DBMS系統,預設都會使用讀提交(Read-Comitted,RC)作為預設隔離級別,如Oracle、SQL Server等,而MySQL卻使用可重複讀

MySQL InnoDB儲存引擎隔離級別及髒、不重複

前記: ORACLE不支援Read Uncommitted和Repeatable Read事務隔離級別; InnoDB預設是RR,使用Next-Key Lock演算法避免幻讀,達到Serializable隔離級別; 隔離級別越低,事務請求所越少或保持鎖的時間越短;

MySQL數據庫事務各隔離級別加鎖情況--read committed && MVCC(轉)

釋放 什麽 表空間 版本 read 存儲引擎 extern 不同 重新 本文轉自https://m.imooc.com/article/details?article_id=17290 感謝作者 上篇記錄了我對MySQL 事務 隔離級別read uncommitted

關於mysql重複的原因的解決(MVCC-多版本併發控制)

第三個隔離級別RR可以解決不可重複度的問題,那什麼是可重複讀: Repeatable Read(可重複讀):一個事務在執行過程中可以看到其他事務已經提交的新插入的記錄(讀已經提交的,其實是讀早於本事務開始且已經提交的),但是不能看到其他事務對已有記錄的更新(即晚於本事務開始的),並且,該事務

SQL Server 中的事務與事務隔離級別以及如何理解髒, 未提交,不可重複產生的過程原因

原本打算寫有關 SSIS Package 中的事務控制過程的,但是發現很多基本的概念還是需要有 SQL Server 事務和事務的隔離級別做基礎鋪墊。所以花了點時間,把 SQL Server 資料庫中的事務概念,ACID 原則,事務中常見的問題,問題造成的原因和事務隔離級別等這些方面的知識好好的整理了一下。

資料庫事務隔離級別,髒、不可重複

資料庫事務的隔離級別有4個,由低到高依次為Read uncommitted 、Read committed 、Repeatable read 、Serializable  ,後面三個可以逐個解決髒讀 、不可重複讀 、幻讀 這幾類問題。 髒讀 不可重複讀 幻讀 Read u

mysql重複例項

mysql的預設事務級別是:可重複讀 其中可重複讀是通過mvcc來實現的又叫快照讀,在事務中的讀操作通過對當前的資料庫中記錄一個版本,以後的讀操作只會讀取記錄的版本,因此相當於對資料庫的資料建立了一個快照資料,因此叫做快照讀,其不用對資料庫中的資料進行加鎖又叫

資料庫事務隔離級別及髒、不可重複的理解

一、資料庫事務正確執行的四個基本要素 1.1ACID原則。   ACID原則是資料庫事務正常執行的四個基本要素,分別指原子性、一致性、獨立性及永續性。   原子性(Atomicity)是指一個事務要麼全部執行,要麼不執行,也就是說一個事務不可能只執

理解資料庫事務隔離級別以及髒, 不可重複,

資料庫事務的4個特性: 原子性(atomic): 都成功或者都失敗;一致性(consistency):事務操作之後,資料庫所處的狀態和業務規則是一致的;比如a,b賬戶相互轉賬之後,總金額不變;隔離性(isolation):操作中的事務不相互影響;永續性(durability

MySQL 重複,差點就我背上了一個 P0 事故!

## 小黑黑的碎碎念 哎,最近有點忙,備考複習不利,明天還要搬家,好難啊!! ![](https://img2020.cnblogs.com/other/1419561/202006/1419561-20200601072354977-768513276.png) 本想著這周鴿了,但是想想還是不行,爬

事務的隔離級別mysql事務隔離級別修改

eat log control 容易 新的 pda mit 全局 nbsp A事務做了操作 沒有提交 對B事務來說 就等於沒做 獲取的都是之前的數據 但是 在A事務中查詢的話 查到的都是操作之後的數據 沒有提交的數據只有自己看得到,並沒有update到數據庫。 查看In

MySQL系列之二四種隔離級別及加鎖

死鎖 開啟 serial 串行化 工作 保存 city innodb sele 事務 1、定義:所有操作必須成功完成,否則在每個操作中所作的所有更改都會備撤銷。 2、事務的ACID 原子性atomicity 一致性consistency 隔離性isola

MYSQL隔離級別 通俗理解 + mysql、oracle默認事務隔離級別

缺省 ... city blog 一場 避免 持久性 user from ★  臟讀    : 讀取了前一事務 未提交 的數據 ;   不可重復讀 : 讀取了前一事務 提交 的數據; ★ 幻讀 與 不可重復讀       common  :都是讀