資料庫的讀現象淺析

liftsail發表於2024-06-13

“讀現象”是多個事務併發執行時,在讀取資料方面可能碰到的狀況。先了解它們有助於理解各隔離級別的含義。其中包括髒讀、不可重複讀和幻讀。

髒讀

髒讀又稱無效資料的讀出,是指在資料庫訪問中,事務T1將某一值修改,然後事務T2讀取該值,此後T1因為某種原因撤銷對該值的修改,這就導致了T2所讀取到的資料是無效的。

髒讀就是指當一個事務正在訪問資料,並且對資料進行了修改,而這種修改還沒有提交(commit)到資料庫中,這時,另外一個事務也訪問這個資料,然後使用了這個資料。因為這個資料是還沒有提交的資料,那麼另外一個事務讀到的這個資料是髒資料,依據髒資料所做的操作可能是不正確的。

舉例說明:

在下面的例子中,事務2修改了一行,但是沒有提交,事務1讀了這個沒有提交的資料。現在如果事務2回滾了剛才的修改或者做了另外的修改的話,事務1中查到的資料就是不正確的了。

事務一 事務二
/* Query 1 */

SELECT age FROM users WHERE id = 1;

/* will read 20 */

/* Query 2 */

UPDATE users SET age = 21 WHERE id = 1;

/* No commit here */

/* Query 1 */

SELECT age FROM users WHERE id = 1;
/* will read 21 */

ROLLBACK;

/* lock-based DIRTY READ */

在這個例子中,事務2回滾後就沒有id是1,age是21的資料了。所以,事務一讀到了一條髒資料。

不可重複讀

不可重複讀,是指在資料庫訪問中,一個事務範圍內兩個相同的查詢卻返回了不同資料。這是由於查詢時系統中其他事務修改的提交而引起的。比如事務T1讀取某一資料,事務T2讀取並修改了該資料,T1為了對讀取值進行檢驗而再次讀取該資料,便得到了不同的結果。

一種更易理解的說法是:在一個事務內,多次讀同一個資料。在這個事務還沒有結束時,另一個事務也訪問該同一資料。那麼,在第一個事務的兩次讀資料之間。由於第二個事務的修改,那麼第一個事務讀到的資料可能不一樣,這樣就發生了在一個事務內兩次讀到的資料是不一樣的,因此稱為不可重複讀,即原始讀取不可重複。

舉例說明:

在基於鎖的併發控制中“不可重複讀(non-repeatable read)”現象發生在當執行SELECT 操作時沒有獲得讀鎖(read locks)或者SELECT操作執行完後馬上釋放了讀鎖; 多版本併發控制中當沒有要求一個提交衝突的事務回滾也會發生“不可重複讀(non-repeatable read)”現象。

事務一 事務二
/* Query 1 */

SELECT * FROM users WHERE id = 1;

/* Query 2 */

UPDATE users SET age = 21 WHERE id = 1;

COMMIT;


/* in multiversion concurrency
control, or lock-based READ COMMITTED */

/* Query 1 */

SELECT * FROM users WHERE id = 1;

COMMIT;

/*lock-based REPEATABLE READ */

在這個例子中,事務2提交成功,因此他對id為1的行的修改就對其他事務可見了。但是事務1在此前已經從這行讀到了另外一個“age”的值。

幻讀

幻讀是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,比如這種修改涉及到表中的“全部資料行”。同時,第二個事務也修改這個表中的資料,這種修改是向表中插入“一行新資料”。那麼,以後就會發生操作第一個事務的使用者發現表中還有沒有修改的資料行,就好象發生了幻覺一樣.一般解決幻讀的方法是增加範圍鎖RangeS,鎖定檢鎖範圍為只讀,這樣就避免了幻讀。

幻讀(phantom read)”是不可重複讀(Non-repeatable reads)的一種特殊場景:當事務沒有獲取範圍鎖的情況下執行SELECT ... WHERE操作可能會發生“幻影讀(phantom read)”。

舉例說明:

當事務1兩次執行SELECT ... WHERE檢索一定範圍內資料的操作中間,事務2在這個表中建立了(如INSERT)了一行新資料,這條新資料正好滿足事務1的“WHERE”子句。

事務一 事務二
/* Query 1 */

SELECT * FROM users
WHERE age BETWEEN 10 AND 30;

/* Query 2 */

INSERT INTO users VALUES ( 3, 'Bob', 27 );

COMMIT;

/* Query 1 */

SELECT * FROM users
WHERE age BETWEEN 10 AND 30;

在這個例子中,事務一執行了兩次相同的查詢操作。但是兩次操作中間事務二向資料庫中增加了一條符合事務一的查詢條件的資料,導致幻讀。

解決方案

要想解決髒讀、不可重複讀、幻讀等讀現象,那麼就需要提高事務的隔離級別。但與此同時,事務的隔離級別越高,併發能力也就越低。所以,還需要讀者根據業務需要進行權衡。

參考資料

維基百科

轉載自https://www.hollischuang.com/archives/900

相關文章