事務(wù)的四個(gè)特性(ACID)
如果一個(gè)數(shù)據(jù)庫聲稱支持事務(wù)的操作,那么該數(shù)據(jù)庫必須要具備以下四個(gè)特性:
原子性(Atomicity)
原子性是指事務(wù)包含的所有操作要么全部成功募寨,要么全部失敗回滾。一旦事務(wù)在運(yùn)行中出現(xiàn)錯(cuò)誤苛谷,直接回滾到未啟動(dòng)事務(wù)前格郁。一致性(Consistency)
一致性是指事務(wù)必須使數(shù)據(jù)庫從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài),也就是說一個(gè)事務(wù)執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)锣尉。多個(gè)事務(wù)并發(fā)的情況下决采,也要保證系統(tǒng)數(shù)據(jù)有如同串行操作一樣的一致效果。隔離性(Isolation)
隔離性是當(dāng)多個(gè)用戶并發(fā)訪問數(shù)據(jù)庫時(shí),數(shù)據(jù)庫為每一個(gè)用戶開啟的事務(wù)爱谁,不能被其他事務(wù)的操作所干擾孝偎,多個(gè)并發(fā)事務(wù)之間要相互隔離,仿佛是系統(tǒng)內(nèi)唯一操作寺旺。
即要達(dá)到這么一種效果:對(duì)于任意兩個(gè)并發(fā)的事務(wù) T1 和 T2势决,在事務(wù) T1 看來,T2 要么在 T1 開始之前就已經(jīng)結(jié)束果复,要么在 T1 結(jié)束之后才開始,這樣每個(gè)事務(wù)都感覺不到有其他事務(wù)在并發(fā)地執(zhí)行走搁。持久性(Durability)
持久性是指一個(gè)事務(wù)一旦被提交了极颓,那么對(duì)數(shù)據(jù)庫中的數(shù)據(jù)的改變就是永久性的,即便是在數(shù)據(jù)庫系統(tǒng)遇到故障的情況下也不會(huì)丟失提交事務(wù)的操作兵琳。
事務(wù)的隔離級(jí)別
先來看看如果不考慮事務(wù)的隔離性骇径,會(huì)發(fā)生的幾種問題:
-
第一類丟失更新(Update Lost)
此種更新丟失是因?yàn)榛貪L的原因,所以也叫回滾丟失破衔。此時(shí)兩個(gè)事務(wù)同時(shí)更新 count,兩個(gè)事務(wù)都讀取到 100嫡丙,事務(wù)一更新成功并提交读第,count=100+1=101,事務(wù)二出于某種原因更新失敗了父泳,然后回滾,事務(wù)二就把 count 還原為它一開始讀到的 100惠窄,此時(shí)事務(wù)一的更新就這樣丟失了。 -
臟讀(Dirty Read)
臟讀是指在一個(gè)事務(wù)處理過程中讀取了另一個(gè)未提交的事務(wù)中的數(shù)據(jù)楞卡。
當(dāng)一個(gè)事務(wù)正在多次修改某個(gè)數(shù)據(jù)擒贸,而在這個(gè)事務(wù)中這多次的修改都還未提交觉渴,這時(shí)一個(gè)并發(fā)的事務(wù)來訪問該數(shù)據(jù),就會(huì)造成兩個(gè)事務(wù)得到的數(shù)據(jù)不一致座韵。例如:用戶 A 向用戶 B 轉(zhuǎn)賬 100 元踢京,對(duì)應(yīng) SQL 命令如下:
update account set money=money+100 where name=’B’; --此時(shí) A 通知 B
update account set money=money-100 where name=’A’;
當(dāng)只執(zhí)行第一條 SQL 時(shí),A 通知 B 查看賬戶瓣距,B 發(fā)現(xiàn)確實(shí)錢已到賬(此時(shí)即發(fā)生了臟讀)蹈丸,而之后無論第二條 SQL 是否執(zhí)行,只要該事務(wù)不提交逻杖,則所有操作都將回滾,那么當(dāng) B 以后再次查看賬戶時(shí)就會(huì)發(fā)現(xiàn)錢其實(shí)并沒有轉(zhuǎn)闻伶。
不可重復(fù)讀(Not Repeatable Read)
不可重復(fù)讀是指在對(duì)于數(shù)據(jù)庫中的某個(gè)數(shù)據(jù)够话,一個(gè)事務(wù)范圍內(nèi)多次查詢卻返回了不同的數(shù)據(jù)值,這是由于在查詢間隔女嘲,被另一個(gè)事務(wù)修改并提交了。
不可重復(fù)讀和臟讀的區(qū)別是澡为,臟讀是某一事務(wù)讀取了另一個(gè)事務(wù)未提交的臟數(shù)據(jù),而不可重復(fù)讀則是讀取了前一事務(wù)提交的數(shù)據(jù)顶别。
在某些情況下,不可重復(fù)讀并不是問題完慧,比如我們多次查詢某個(gè)數(shù)據(jù)當(dāng)然以最后查詢得到的結(jié)果為主剩失。但在另一些情況下就有可能發(fā)生問題,例如對(duì)于 A 和 B 依次查詢就可能不同拴孤,影響后續(xù)的操作。第二類丟失更新(Second Update Lost)
此種更新丟失是因?yàn)楦卤黄渌聞?wù)給覆蓋了鞭执,也可以叫覆蓋丟失芒粹。舉個(gè)例子,兩個(gè)事務(wù)同時(shí)更新 count化漆,都讀取 100 這個(gè)初始值座云,事務(wù)一先更新成功并提交,count=100+1=101疙教,事務(wù)二后更新成功并提交,count=100+1=101限佩,由于事務(wù)二 count 還是從 100 開始增加裸弦,事務(wù)一的更新就這樣丟失了。虛讀(幻讀)(Phantom Read)
幻讀是事務(wù)非獨(dú)立執(zhí)行時(shí)發(fā)生的一種現(xiàn)象理疙。例如事務(wù) T1 對(duì)一個(gè)表中所有的行的某個(gè)數(shù)據(jù)項(xiàng)做了從 “1” 修改為 “2” 的操作窖贤,這時(shí)事務(wù) T2 又對(duì)這個(gè)表中插入了一行數(shù)據(jù)項(xiàng)贰锁,而這個(gè)數(shù)據(jù)項(xiàng)的數(shù)值還是為 “1” 并且提交給數(shù)據(jù)庫背伴。而操作事務(wù) T1 的用戶如果再查看剛剛修改的數(shù)據(jù),會(huì)發(fā)現(xiàn)還有一行沒有修改憔四,其實(shí)這行是從事務(wù) T2 中添加的览闰,就好像產(chǎn)生幻覺一樣,這就是發(fā)生了幻讀压鉴。
幻讀和不可重復(fù)讀都是讀取了另一條已經(jīng)提交的事務(wù),所不同的是不可重復(fù)讀查詢的都是同一個(gè)數(shù)據(jù)項(xiàng)掩幢,而幻讀針對(duì)的是一批數(shù)據(jù)整體(比如數(shù)據(jù)的個(gè)數(shù))上鞠。
現(xiàn)在來看看 MySQL 數(shù)據(jù)庫為我們提供的四種隔離級(jí)別以及可避免的問題:
隔離級(jí)別 | 第一類丟失更新 | 臟讀 | 不可重復(fù)讀 | 第二類丟失更新 | 幻讀 |
---|---|---|---|---|---|
Serializable (串行化) |
√ | √ | √ | √ | √ |
Repeatable read (可重復(fù)讀) |
√ | √ | √ | √ | × |
Read committed (讀已提交) |
√ | √ | × | × | × |
Read uncommitted (讀未提交) |
× | × | × | × | × |
在 MySQL 數(shù)據(jù)庫中默認(rèn)的隔離級(jí)別為 Repeatable read(可重復(fù)讀)芍阎。