自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

InnoDB的RR到底有沒有解決幻讀?

數(shù)據(jù)庫 MariaDB
在Repeatable Read隔離級別下,通過MVCC機制解決了快照讀導(dǎo)致的幻讀問題。在該隔離級別下,進行快照讀時僅在第一次進行數(shù)據(jù)查詢,隨后直接讀取快照,因此不會發(fā)生幻讀。

在InnoDB中,Repeatable Read(重復(fù)讀)隔離級別通過間隙鎖和MVCC機制解決了大部分的幻讀問題,但并非所有幻讀都能被解決。要徹底解決幻讀,需要使用Serializable(可串行化)隔離級別。

在Repeatable Read隔離級別下,通過間隙鎖解決了部分當前讀導(dǎo)致的幻讀問題。通過添加間隙鎖來鎖定記錄之間的間隙,以防止新數(shù)據(jù)的插入。

在Repeatable Read隔離級別下,通過MVCC機制解決了快照讀導(dǎo)致的幻讀問題。在該隔離級別下,進行快照讀時僅在第一次進行數(shù)據(jù)查詢,隨后直接讀取快照,因此不會發(fā)生幻讀。

然而,若兩個事務(wù)操作如下:事務(wù)1首先進行快照讀,然后事務(wù)2插入一條記錄并提交,在事務(wù)1之后通過更新操作這個新插入的記錄,這樣可以成功更新,這就是幻讀的一種情況。

另外一個場景是,若兩個事務(wù)的順序為:事務(wù)1先進行快照讀,接著事務(wù)2插入了一條記錄并提交,在事務(wù)1進行當前讀后,再次進行快照讀也會導(dǎo)致幻讀的發(fā)生。

MVCC解決幻讀

MVCC,即多版本并發(fā)控制(Multiversion Concurrency Control),類似于數(shù)據(jù)庫鎖,是一種并發(fā)控制的解決方案。它主要用于解決讀-寫并發(fā)的情況。

我們了解,在MVCC中存在兩種讀取方式:快照讀和當前讀。

快照讀指的是讀取快照數(shù)據(jù),即在生成快照的那一瞬間的數(shù)據(jù)。例如,通常情況下我們使用的普通SELECT語句在不加鎖的情況下就是一種快照讀。

在可重復(fù)讀(RC)中,每次讀取都會重新生成一個快照,始終讀取行的最新版本。在可重復(fù)讀(RR)中,快照會在事務(wù)第一次執(zhí)行SELECT語句時生成,只有在本事務(wù)中對數(shù)據(jù)進行更改才會更新快照。

因此,在RR隔離級別下,同一事務(wù)中的多次查詢不會檢索到其他事務(wù)的更改內(nèi)容,因此能夠解決幻讀問題。

若我們將事務(wù)隔離級別設(shè)置為RR,由于MVCC的機制,就可以解決幻讀問題。

有這樣一張表:

CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT,
    gmt_create DATETIME NOT NULL,
    age INT NOT NULL,
    name VARCHAR(16) NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB;

INSERT INTO users(gmt_create,age,name) values(now(),18,'Paidaxing');
INSERT INTO users(gmt_create,age,name) values(now(),28,'Paidaxing2023');
INSERT INTO users(gmt_create,age,name) values(now(),38,'Paidaxing666');

執(zhí)行如下事務(wù)時序:

事務(wù)1


SET session TRANSACTION ISOLATION LEVEL REPEATABLE READ;


BEGIN;


SELECT * FROM users WHERE AGE > 10 AND AGE <30;圖片



BEGIN;


INSERT INTO users(gmt_create, age, name) values(now(), 20, 'Paidaxing999');圖片


COMMIT;

SELECT * FROM users WHERE AGE > 10 AND AGE < 30;圖片


可以觀察到,在同一個事務(wù)中,兩次查詢的結(jié)果是相同的。在可重復(fù)讀(RR)級別下,由于采用了快照讀,第二次查詢實際上是讀取的快照數(shù)據(jù)。

間隙鎖與幻讀

我們已經(jīng)討論了MVCC如何解決了可重復(fù)讀(RR)級別下的快照讀造成的幻讀問題,那么在當前讀取(READ COMMITTED)下,如何解決幻讀問題呢?

當前讀取即讀取最新數(shù)據(jù),因此,鎖定的SELECT語句,或者進行數(shù)據(jù)的插入、刪除、更新都屬于當前讀取操作,例如:

SELECT * FROM xx_table LOCK IN SHARE MODE;

SELECT * FROM xx_table FOR UPDATE;

INSERT INTO xx_table ...

DELETE FROM xx_table ...

UPDATE xx_table ...

舉一個下面的例子:

事務(wù)1

事務(wù)2

SET session TRANSACTION ISOLATION LEVEL REPEATABLE READ;


BEGIN;


SELECT * FROM users WHERE AGE > 10 AND AGE < 30 for update;圖片



BEGIN;


INSERT INTO users(gmt_create, age, name) values(now(), 20, 'Paidaxing999');


阻塞圖片

在可重復(fù)讀(RR)級別下,當我們使用SELECT … FOR UPDATE時,會進行鎖定操作。這不僅會對行記錄進行加鎖,還會對記錄之間的間隙進行加鎖,這就是所謂的間隙鎖。

由于記錄之間的間隙被鎖定,事務(wù)2的插入操作被阻塞,直到事務(wù)1釋放鎖才得以成功執(zhí)行。

由于事務(wù)2無法成功插入數(shù)據(jù),因此幻讀現(xiàn)象得以避免。因此,在可重復(fù)讀(RR)級別中,通過引入間隙鎖的方式,成功規(guī)避了幻讀現(xiàn)象的發(fā)生。

解決不了的幻讀

前面我們討論了快照讀(無鎖查詢)和當前讀(有鎖查詢)是如何解決幻讀問題的。然而,上面提到的例子并非幻讀的全部情況。

我們知道MVCC只能解決快照讀導(dǎo)致的幻讀問題,那么如果一個事務(wù)中發(fā)生了當前讀,在另一個事務(wù)插入數(shù)據(jù)前未加間隙鎖,會發(fā)生什么呢?

接下來,我們稍作修改上面的SQL代碼,采用當前讀方式來查詢數(shù)據(jù):

事務(wù)1

事務(wù)2

SET session TRANSACTION ISOLATION LEVEL REPEATABLE READ;


BEGIN;


SELECT * FROM users WHERE AGE > 10 AND AGE <30;圖片



BEGIN;


INSERT INTO users(gmt_create, age, name) values(now(), 20, 'Paidaxing999');圖片


COMMIT;

SELECT * FROM users WHERE AGE > 10 AND AGE < 30;圖片


SELECT * FROM users WHERE AGE > 10 AND AGE < 30 for update;圖片


在上面的例子中,在事務(wù)1中,我們并未在事務(wù)剛啟動時立即加鎖,而是進行了一次普通的查詢,隨后事務(wù)2成功插入數(shù)據(jù)后,事務(wù)1再進行了兩次查詢。

我們觀察到,事務(wù)1后兩次查詢的結(jié)果完全不同。在沒有加鎖的情況下,即快照讀時,讀取的數(shù)據(jù)與第一次查詢結(jié)果相同,從而避免了幻讀現(xiàn)象。但第二次查詢執(zhí)行了鎖定操作,即當前讀,因此讀取到的數(shù)據(jù)中包含了其他事務(wù)提交的數(shù)據(jù),導(dǎo)致了幻讀的發(fā)生。

倘若您理解了上述例子以及當前讀的概念,您將很容易意識到,下面的這個案例事實上也會導(dǎo)致幻讀的發(fā)生:

事務(wù)1

事務(wù)2

SET session TRANSACTION ISOLATION LEVEL REPEATABLE READ;


BEGIN;


SELECT * FROM users WHERE AGE > 10 AND AGE <30;圖片



BEGIN;


INSERT INTO users(gmt_create, age, name) values(now(), 20, 'Paidaxing999');圖片


COMMIT;

SELECT * FROM users WHERE AGE > 10 AND AGE <30;圖片


UPDATE users set name = "Paidaxing888" where age = 20;圖片


SELECT * FROM users WHERE AGE > 10 AND AGE <30;圖片


這里產(chǎn)生幻讀的原因和前面的例子實際上是相同的。即,MVCC只能解決快照讀中的幻讀問題,而對于當前讀(例如 SELECT FOR UPDATE、UPDATE、DELETE 等操作)仍會導(dǎo)致幻讀的產(chǎn)生。在同一個事務(wù)中同時進行快照讀和當前讀操作時,將導(dǎo)致幻讀的發(fā)生。

UPDATE 語句也屬于當前讀操作,因此它有可能讀取到其他事務(wù)提交的結(jié)果。

為何事務(wù)1最后一次查詢和倒數(shù)第二次查詢的結(jié)果會不同呢?

原因在于根據(jù)快照讀的定義,在可重復(fù)讀級別下,如果在本事務(wù)中發(fā)生了數(shù)據(jù)修改,將會更新快照數(shù)據(jù),因此最后一次查詢的結(jié)果也會相應(yīng)地發(fā)生變化。

如何避免幻讀

了解了幻讀產(chǎn)生的情境以及無法解決的幾種情況后,讓我們總結(jié)一下如何解決幻讀的問題。

首先,若欲徹底解決幻讀問題,在 InnoDB 中唯一可選的隔離級別是 Serializable(可串行化)級別。

圖源:MySQL 8.0 參考手冊圖源:MySQL 8.0 參考手冊

若希望在一定程度上解決或避免幻讀,可考慮使用可重復(fù)讀(RR)隔離級別,但讀提交(RC)和讀未提交(RU)級別肯定不可行。

在可重復(fù)讀級別中,盡量使用快照讀(無鎖查詢),這樣不僅可以減少鎖沖突、提高并發(fā)度,還能避免幻讀問題的發(fā)生。

在高并發(fā)場景中若必須加鎖,應(yīng)在事務(wù)開始時立即加鎖,這將引入間隙鎖,有效地避免幻讀。

然而,值得注意的是,間隙鎖是引發(fā)死鎖的重要因素,因此在使用時需要謹慎對待。

責任編輯:武曉燕 來源: 碼上遇見你
相關(guān)推薦

2022-12-27 10:02:38

MVCC機制Innodb

2021-08-26 06:58:15

Innodb RR隔離級別

2023-08-09 17:22:30

MVCCMySQL數(shù)據(jù)

2019-05-28 13:50:27

MySQL幻讀數(shù)據(jù)庫

2019-12-06 14:50:49

APP錄音隱私

2024-04-24 08:26:35

事務(wù)數(shù)據(jù)InnoDB

2012-09-21 09:16:48

開源云平臺云計算OpenStack C

2015-04-09 13:32:16

編程編程前途

2024-03-20 08:12:12

分庫分表數(shù)據(jù)

2022-06-30 08:00:00

MySQL關(guān)系數(shù)據(jù)庫開發(fā)

2024-05-24 11:54:11

2022-07-14 10:00:21

數(shù)據(jù)價值

2022-02-17 21:13:08

MySQL架構(gòu)體系

2021-07-26 10:28:13

MySQL事務(wù)隔離

2023-12-27 18:16:39

MVCC隔離級別幻讀

2021-11-30 06:56:59

MySQL幻讀查詢

2023-06-05 00:28:24

MySQL數(shù)據(jù)庫非鎖定讀

2020-08-11 15:42:39

微軟小冰Cortana

2021-01-20 20:49:31

AI

2024-05-13 11:46:33

MySQL數(shù)據(jù)庫
點贊
收藏

51CTO技術(shù)棧公眾號