既然MySQL中InnoDB使用MVCC,為什麼REPEATABLE READ不能消除幻讀?

時間 2021-05-12 06:21:50

1樓:SHQHDMR

mysql5.6官方文件原話,RR用next-key locks防止幻讀

By default,InnoDBoperates inREPEATABLE READ

transaction isolation level and with theinnodb_locks_unsafe_for_binlog

system variable disabled. In this case,InnoDBuses next-key locks for searches and index scans, which prevents phantom rows (seeSection 14.7.

4, 「Phantom Rows」).

幻讀,官網的定義就是事務中相同讀語句在不同時間讀到了不一樣的結果,與不可重複讀的定義一樣,但是作用原理不同,幻讀是gap lock缺失導致,不可重複讀是一致性檢視的生成和使用時機導致

不用扯什麼當前讀(select * from t for update;)跟快照讀(select * from t;)的結果不一樣,兩個讀,它們語句都不一樣,比結果有什麼意義

2樓:黃杰

MVCC的機制。快照讀不會產生幻讀。因為ReadView生成後就不會發生變化

每次執行都會讀取最新的記錄。(假設要update一條記錄,但是在另乙個事務中已經delete掉這條資料並且commit了,如果update就會產生衝突,所以在update的時候需要知道最新的資料。)

結論:MVCC的機制會使Select語句的快照讀避免幻讀,但是對於當前讀的操作依然會出現幻讀。

例子:假如A事務正在查詢id<10的所有資料,只存在id為1~7的資料,8、9並不存在,此時B事務向資料庫插入id為8的資料,那麼事務A就會出現幻讀現象,本來是不存在id為8的資料的,但是像出現幻覺一樣讀取到了,這就是幻讀。

解決辦法:加上next-key鎖(也就是行鎖+gap鎖),gap鎖會鎖著id為8、9的兩個位置,阻止事務A讀取資料的時候,事務B向資料庫插入資料,這樣就避免幻讀了。

結論:在快照讀情況下,MySQL通過mvcc來避免幻讀。

在當前讀情況下,MySQL通過next-key來避免幻讀

MVCC原理詳解

InnoDB基於MVCC和next-key鎖解決幻讀問題 - osc_8j0twt2u的個人空間 - OSCHINA - 中文開源技術交流社群

MVCC解決幻讀_我要提問_牛客網

RDD2DAG的回答

3樓:lxyscls

補兩個圖

4樓:light0x00

RR級別產生幻讀,往往都是直接或間接執行了「當前讀」,導致ReadView更新,所以會讀到新的

1.事務A中執行修改

update

user

setcol1

='new_val'

whereid=

1;結果:

QueryOK,

0rows

affected(0

.00sec)

Rows

matched:0

Changed:0

Warnings:0

2.事務B插入一條資料,並提交.

begin

;insert

into

user

values

(null

,'A'

);commit

;3. 事務A中再次執行修改

update

user

setcol1

='new_val'

whereid=

1;結果:

QueryOK,

1rows

affected(0

.00sec)

Rows

matched:1

Changed:1

Warnings:0

問題的本質是因為 update語句的查詢階段相當於select ... for update,這會更新事務A的 ReadView, 從而可以讀到「其他事務已提交的修改」.

5樓:陳廣勝

第一,這個問題取決於我們如何定義「幻象」異常。

早期的資料庫都是單版本的,這個定義沒什麼問題。但是,由於多版本的存在,情形就變得複雜了。RR隔離級別在MVCC實現的資料庫中,一般會被實現成快照(SNAPSHOT),這就可能會產生另一種異常。

由於事務會讀到不同的版本,對於相同區間的查詢,事務可能會錯過某些滿足該條件的併發地插入的記錄,該事務只有在插入這條記錄的事務提交後才能看到這條記錄。進而產生的問題就是,事務本應該讀到的資料,卻沒有被讀到。

例如,assignments表有四列(eid, pid, workdate, hours)。assignments表示的是給employee(eid)分配project(pid),並記錄某個工作日(workdate)的工時(hours)。限制每個工作日工時不超過8小時。

assign表示分配工時的儲存過程,假設eid為1的員工已有兩個project, 工時分別為4,1。有兩個併發的事務T1, T2, 同時執行assign。當T1,T2開始時,對於滿足條件eid = 1 and workdate = '2019.

7.11' 的元組,拿到的是相同的快照,它們都判定插入一條工時為4的元組不會使當日工時大於8。

這個異常不滿足上面對幻讀的定義,然而這個事務排程卻是不正確的。一些文獻把這種異常也稱為幻象(write skew style phantom)[2]。

MVCC資料庫無法避免這種異常。如果要避免這種異常,就必須要提高隔離級別到可序列化。可序列化的實現,在MySQL中是通過對讀加鎖(Gap Lock);在PG中是使用SSI演算法,通過驗證連續的RW依賴檢測是否事務是否可序列化[3]。

第二,MySQL有乙個比較特殊的情形,就是它允許覆蓋更新這種行為(不遵守first commit win rule),這讓它產生了另一種幻像。

如下面的例子,有兩個事務,在RR隔離級別下, select是沒有幻讀的,但select for update卻會產生幻讀。因為select是讀,通過時間戳讀快照,事務2讀不到事務1的寫入。而select for update被認為是寫,是可以更新已提交資料的,所以讀到的是最新版本,事務2可以讀到事務1的寫入。

PostgreSQL是沒有這個現象的。

事務1事務2

mysql> start transactionmysql> start transaction;

Query OK, 0 rows affected (0.00 secQuery OK, 0 rows affected (0.00 secmysql> select * from tEmpty set (0.

00 sec)

mysql> insert into t values(1);

Query OK, 1 row affected (0.00 sec)

mysql> commit;

Query OK, 0 rows affected (0.01 secno phantommysql> select * from tEmpty set (0.00 secphantommysql> select * from t for updatec11 row in set (0.

00 secupdate committed rowmysql> update t set c=2Query OK, 0 rows affected (0.00 secRows matched: 1 Changed:

0 Warnings: 0mysql> commitQuery OK, 0 rows affected (0.01 sec)

MySQL中inner join 和 cross join 的區別?

MySQL 5.7 文件中的表述 對於 MySQL,JOIN,CROSS JOIN,INNER JOIN 完全等價 one flower 在MySQL中,CROSS JOIN語法上等價於INNER JOIN 它們可以彼此代替。在標準SQL中,它們不等價。INNER JOIN結合ON子句使用 CROS...

python從mysql中取到time型別欄位的值變成timedelta型別了,如何轉換為時間格式?

管理他爹 自問自答,上面回答的答案都是答非所問,最後還是在stackoverflow上找到了答案,正確解決方式如下 value datetime.timedelta 0,64800 datetime.datetime.min value time 青牛 來個例子 usr bin env python...

mysql中資料已經完成了binlog的寫入儲存引擎的提交,那麼資料算是已經落盤了嗎?

趙偉 不一定,要看你的 innodb flush log at trxcommit 和sync binlog 的設定。只有兩者都是1,乙個事務提交後才意味著其改動會持久化。有人為了效能把兩者都設定為0,那意味著一旦mysqld 程序不是正常退出,比如被kill掉,或者crash,即使作業系統保持正常...