詳解-臟讀、幻讀與不可重復(fù)讀
最近在讀 《MySQL 技術(shù)內(nèi)幕 InnoDB 存儲(chǔ)引擎》,里面提到的各種概念都很新鮮,以前聽(tīng)說(shuō)過(guò)臟讀、幻讀、不可重復(fù)讀,但是對(duì)于概念不甚了解,于是查了一下,這里做個(gè)筆記。
數(shù)據(jù)庫(kù)事務(wù)特征
數(shù)據(jù)庫(kù)事務(wù)特征,即 ACID:
A Atomicity 原子性
事務(wù)是一個(gè)原子性質(zhì)的操作單元,事務(wù)里面的對(duì)數(shù)據(jù)庫(kù)的操作要么都執(zhí)行,要么都不執(zhí)行,
C Consistent 一致性
在事務(wù)開(kāi)始之前和完成之后,數(shù)據(jù)都必須保持一致?tīng)顟B(tài),必須保證數(shù)據(jù)庫(kù)的完整性。也就是說(shuō),數(shù)據(jù)必須符合數(shù)據(jù)庫(kù)的規(guī)則。
I Isolation 隔離性
數(shù)據(jù)庫(kù)允許多個(gè)并發(fā)事務(wù)同事對(duì)數(shù)據(jù)進(jìn)行操作,隔離性保證各個(gè)事務(wù)相互獨(dú)立,事務(wù)處理時(shí)的中間狀態(tài)對(duì)其它事務(wù)是不可見(jiàn)的,以此防止出現(xiàn)數(shù)據(jù)不一致?tīng)顟B(tài)??赏ㄟ^(guò)事務(wù)隔離級(jí)別設(shè)置:包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復(fù)讀(repeatable read)和串行化(Serializable)
D Durable 持久性
一個(gè)事務(wù)處理結(jié)束后,其對(duì)數(shù)據(jù)庫(kù)的修改就是永久性的,即使系統(tǒng)故障也不會(huì)丟失。
MySQL 數(shù)據(jù)隔離級(jí)別
首先 MySQL 里有四個(gè)隔離級(jí)別:Read uncommttied(可以讀取未提交數(shù)據(jù))、Read committed(可以讀取已提交數(shù)據(jù))、Repeatable read(可重復(fù)讀)、Serializable(可串行化)。
在 InnoDB 中,默認(rèn)為 Repeatable 級(jí)別,InnoDB 中使用一種被稱為 next-key locking 的策略來(lái)避免幻讀(phantom)現(xiàn)象的產(chǎn)生。
使用 select @@tx_isolation; 可以查看 MySQL 默認(rèn)的事務(wù)隔離級(jí)別。
不同的事務(wù)隔離級(jí)別會(huì)導(dǎo)致不同的問(wèn)題:

臟讀、幻讀、不可重復(fù)讀的概念
臟讀
所謂臟讀是指一個(gè)事務(wù)中訪問(wèn)到了另外一個(gè)事務(wù)未提交的數(shù)據(jù),如下圖:
如果會(huì)話 2 更新 age 為 10,但是在 commit 之前,會(huì)話 1 希望得到 age,那么會(huì)獲得的值就是更新前的值?;蛘呷绻麜?huì)話
? ? ? ?2 更新了值但是執(zhí)行了 rollback,而會(huì)話 1 拿到的仍是 10。這就是臟讀。
幻讀
一個(gè)事務(wù)讀取2次,得到的記錄條數(shù)不一致:
上圖很明顯的表示了這個(gè)情況,由于在會(huì)話 1 之間插入了一個(gè)新的值,所以得到的兩次數(shù)據(jù)就不一樣了。
不可重復(fù)讀
一個(gè)事務(wù)讀取同一條記錄2次,得到的結(jié)果不一致:

由于在讀取中間變更了數(shù)據(jù),所以會(huì)話 1 事務(wù)查詢期間的得到的結(jié)果就不一樣了。
解決方案
解決方案也就是上文提到的四種隔離級(jí)別,他們可以最大程度避免以上三種情況的發(fā)生:
未授權(quán)讀取
也稱為讀未提交(Read Uncommitted):允許臟讀取,但不允許更新丟失。如果一個(gè)事務(wù)已經(jīng)開(kāi)始寫(xiě)數(shù)據(jù),則另外一個(gè)事務(wù)則不允許同時(shí)進(jìn)行寫(xiě)操作,但允許其他事務(wù)讀此行數(shù)據(jù)。該隔離級(jí)別可以通過(guò)“排他寫(xiě)鎖”實(shí)現(xiàn)。
授權(quán)讀取
也稱為讀提交(Read Committed):允許不可重復(fù)讀取,但不允許臟讀取。這可以通過(guò)“瞬間共享讀鎖”和“排他寫(xiě)鎖”實(shí)現(xiàn)。讀取數(shù)據(jù)的事務(wù)允許其他事務(wù)繼續(xù)訪問(wèn)該行數(shù)據(jù),但是未提交的寫(xiě)事務(wù)將會(huì)禁止其他事務(wù)訪問(wèn)該行。
可重復(fù)讀?。≧epeatable Read)
可重復(fù)讀?。≧epeatable Read):禁止不可重復(fù)讀取和臟讀取,但是有時(shí)可能出現(xiàn)幻讀數(shù)據(jù)。這可以通過(guò)“共享讀鎖”和“排他寫(xiě)鎖”實(shí)現(xiàn)。讀取數(shù)據(jù)的事務(wù)將會(huì)禁止寫(xiě)事務(wù)(但允許讀事務(wù)),寫(xiě)事務(wù)則禁止任何其他事務(wù)。
序列化(Serializable)
序列化(Serializable):提供嚴(yán)格的事務(wù)隔離。它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個(gè)接著一個(gè)地執(zhí)行,不能并發(fā)執(zhí)行。僅僅通過(guò)“行級(jí)鎖”是無(wú)法實(shí)現(xiàn)事務(wù)序列化的,必須通過(guò)其他機(jī)制保證新插入的數(shù)據(jù)不會(huì)被剛執(zhí)行查詢操作的事務(wù)訪問(wèn)到。
隔離級(jí)別越高,越能保證數(shù)據(jù)的完整性和一致性,但是對(duì)并發(fā)性能的影響也越大。對(duì)于多數(shù)應(yīng)用程序,可以優(yōu)先考慮把數(shù)據(jù)庫(kù)系統(tǒng)的隔離級(jí)別設(shè)為Read Committed。它能夠避免臟讀取,而且具有較好的并發(fā)性能。盡管它會(huì)導(dǎo)致不可重復(fù)讀、幻讀和第二類丟失更新這些并發(fā)問(wèn)題,在可能出現(xiàn)這類問(wèn)題的個(gè)別場(chǎng)合,可以由應(yīng)用程序采用悲觀鎖或樂(lè)觀鎖來(lái)控制。
