新聞中心
什么是事務(wù)?
- 事務(wù)是一個(gè)不可分割的工作單元,工作單元要么工作完成,要么什么也不做。
- 從應(yīng)用層面來(lái)說(shuō)一個(gè)事務(wù)對(duì)應(yīng)了一個(gè)完整的業(yè)務(wù)功能。
- 從數(shù)據(jù)庫(kù)層面的來(lái)講事務(wù)就是由一批DML語(yǔ)句構(gòu)成。
事務(wù)的分類(lèi)
MySQL的InnoDB存儲(chǔ)引擎支持扁平事務(wù)、帶有保存點(diǎn)的事務(wù)、鏈?zhǔn)聞?wù)、分布式事務(wù)。

- 扁平事務(wù)(Flat Transactions)
扁平事務(wù)應(yīng)用最為廣泛,實(shí)現(xiàn)最為簡(jiǎn)單,扁平事務(wù)的所有操作都是在同一個(gè)層級(jí),這些操作要么全部成功,要么全部回滾,不能存在部分提交或者部分回滾的的場(chǎng)景。
扁平事務(wù)
- 帶保存點(diǎn)的扁平事務(wù)(Flat Transactions with Sacepoint)
扁平事務(wù)的限制就在于不能部分回滾或者提交,而有的場(chǎng)景是這么做是代價(jià)非常大的。比如我們舉個(gè)例子:
我們玩生存類(lèi)游戲,如果我們意外失敗就必須從出生地開(kāi)始玩,那么這會(huì)是讓人崩潰的,我們希望有一個(gè)游戲存檔,如果游戲失敗我們可以從最近的一個(gè)存檔重新加載游戲。
帶保存點(diǎn)的扁平事務(wù)就是,除了支持扁平事務(wù)的操作外,允許事務(wù)執(zhí)行過(guò)程中回滾到該事務(wù)較早的一個(gè)狀態(tài),而這個(gè)較早的狀態(tài)就是保存點(diǎn)來(lái)記錄的。
帶保存點(diǎn)的扁平事務(wù)
- 鏈?zhǔn)聞?wù)(Chained Transaction)
鏈?zhǔn)聞?wù)是一種保存點(diǎn)事務(wù)的變種,兩者的最大區(qū)別是,帶保存點(diǎn)的事務(wù)可以回滾到較早前的任意保存點(diǎn),而鏈?zhǔn)绞聞?wù)只能回滾到最近一個(gè)保存點(diǎn);帶保存點(diǎn)的事務(wù)因?yàn)樾枰貪L到任意保存點(diǎn),固其事務(wù)執(zhí)行期間所占用的資源是不會(huì)被釋放的,而鏈?zhǔn)聞?wù)則在執(zhí)行完成當(dāng)前節(jié)點(diǎn)后會(huì)釋放掉不需要的資源,并將下一個(gè)節(jié)點(diǎn)需要的資源隱士傳遞下去。鏈?zhǔn)聞?wù)可以參考Flink流式計(jì)算的Checkpoint機(jī)制,兩者非常的相似。
鏈?zhǔn)聞?wù)
- 嵌套事務(wù)(Nested Transaction)
嵌套事務(wù)顧名思義,事務(wù)結(jié)構(gòu)看上去就像一棵樹(shù),根節(jié)點(diǎn)就是一個(gè)頂層事務(wù),所有的葉子節(jié)點(diǎn)都是扁平事務(wù)(也就是說(shuō)葉子節(jié)點(diǎn)才是真正干活兒的),事務(wù)的嵌套層級(jí)不受限制。子事務(wù)可以提交也可以回滾,但是其提交不會(huì)立即生效,只有在頂層事務(wù)提交之后所有子事務(wù)才會(huì)被真正的提交。
嵌套事務(wù)
- 分布式事務(wù)(Distributed Transactions)
分布式事務(wù)是指一個(gè)在分布式環(huán)境下運(yùn)行的扁平事務(wù),在本章中主要介紹本地事務(wù),分布式事務(wù)我會(huì)在后續(xù)章節(jié)是介紹。
事務(wù)的ACID特性
- A(Atomicity)原子性:整個(gè)事務(wù)操作是一個(gè)完整的不可分割的整體,只有事務(wù)中的所有操作都執(zhí)行成功,事務(wù)才算執(zhí)行成功,否則就要回滾到事務(wù)執(zhí)行前的狀態(tài),即要么全部都做,要么全都不做。
例如:轉(zhuǎn)賬場(chǎng)景,自己賬戶(hù)扣除轉(zhuǎn)賬額度與對(duì)方賬戶(hù)收到轉(zhuǎn)賬這兩個(gè)操作必須是原子的。
- C(Consistency)一致性:事務(wù)將數(shù)據(jù)庫(kù)從一種狀態(tài)轉(zhuǎn)變?yōu)榱硪环N狀態(tài),在事務(wù)執(zhí)行前和事務(wù)執(zhí)行后,數(shù)據(jù)庫(kù)的完整性約束沒(méi)有被破壞。
例如:用戶(hù)表的用戶(hù)ID列有unique約束,即用戶(hù)ID不可重復(fù),如果事務(wù)執(zhí)行插入了一樣的用戶(hù)ID,那么就產(chǎn)生了不一致的狀態(tài)。
- I(Isolation)隔離性:隔離性(又稱(chēng)并發(fā)控制)非常好理解,就是兩個(gè)事務(wù)之間不能相互影響,即當(dāng)前事務(wù)提交之前所作出的修改對(duì)其他事務(wù)都不可見(jiàn),上一章我們講到了MySQL鎖,它就可以起到控制并發(fā)的作用。
- D(Durability)持久性:持久性是指事務(wù)一旦提交,其結(jié)果就是永久性的,即使是服務(wù)器宕機(jī),數(shù)據(jù)也必須能夠得到恢復(fù),除了硬件故障,數(shù)據(jù)物理?yè)p壞,否則必須保證事務(wù)執(zhí)行結(jié)果的永久性,也就是保證高可靠性(High Reliablility)。
事務(wù)如何實(shí)現(xiàn)
事務(wù)的原子性、一致性、持久性通過(guò)redo log與undo log來(lái)完成,事務(wù)的隔離性由鎖與MVCC來(lái)完成。
Redo log(重做日志)
Redo log是用來(lái)實(shí)現(xiàn)事務(wù)的持久性,為了更好的讀寫(xiě)性能,InnoDB會(huì)將數(shù)據(jù)緩存在內(nèi)存中,對(duì)磁盤(pán)數(shù)據(jù)的修改也會(huì)落后于內(nèi)存,如果進(jìn)程或系統(tǒng)崩潰,則數(shù)據(jù)面臨丟失的風(fēng)險(xiǎn),這時(shí)重做日志就起到了保證數(shù)據(jù)的一致性與持久性作用。重做日志主要記錄了以頁(yè)為單位的數(shù)據(jù)修改信息,其結(jié)構(gòu)如下:
redo log 結(jié)構(gòu)
- 重做日志在Buffer中是連續(xù)寫(xiě)入的,Buffer中的數(shù)據(jù)會(huì)適時(shí)地刷新到物理文件中;
- 文件順序?qū)懭?,每個(gè)事務(wù)的重做日志追加到文件末尾;
- 單個(gè)文件大小固定,寫(xiě)滿(mǎn)以后會(huì)切回到文件組的下一個(gè)文件;
- 重做日志文件組的文件個(gè)數(shù)是固定的,寫(xiě)完最后一個(gè)文件則繼續(xù)回到第一個(gè)文件開(kāi)始寫(xiě)入;
- 每個(gè)重做文件有固定2K的文件頭,文件頭的之后是以一個(gè)個(gè)512bytes的Block,每個(gè)Block有16bytes的頭尾信息;
- 重做日志有一個(gè)全局的日志序列號(hào)(LSN:Log Sequence Number),單調(diào)遞增,表示事務(wù)寫(xiě)入的重做日志的字節(jié)總量,也就是一個(gè)日志偏移量。
Undo log(回滾日志)
重做日志記錄了事務(wù)的行為,可以在需要的時(shí)候?qū)?yè)進(jìn)行“重做”,但是事務(wù)有時(shí)是需要被回滾的,當(dāng)語(yǔ)句執(zhí)行失敗或者用戶(hù)請(qǐng)求回滾,就可以通過(guò)undo log將數(shù)據(jù)回滾到修改前的樣子,undo log是存儲(chǔ)了行記錄的變更。其主要包含兩類(lèi)undo log:
兩種undo log結(jié)構(gòu)
- insert undo log:insert操作時(shí)產(chǎn)生,只對(duì)當(dāng)前事務(wù)本身可見(jiàn),在事務(wù)提交之后可直接刪除。
- update undo log:delete與update操作產(chǎn)生,需要提供歷史版本,為后續(xù)章節(jié)要講到的MVCC服務(wù),其交由purge線(xiàn)程統(tǒng)一刪除。
- undo log需要通過(guò)group commit 操作將數(shù)據(jù)fsync到磁盤(pán),以保證事務(wù)的持久性。
下面是一個(gè)事務(wù)與undo log的關(guān)系結(jié)構(gòu):
事務(wù)與undo log關(guān)系結(jié)構(gòu)
事務(wù)隔離
事務(wù)在并發(fā)場(chǎng)景下很難保證事務(wù)的隔離性一致性,主要有以下一些事務(wù)的并發(fā)一致性問(wèn)題。
事務(wù)并發(fā)問(wèn)題
- 臟讀(Dirty Read):事務(wù)A讀取了另外一個(gè)并行事務(wù)B未提交的數(shù)據(jù)。
- 不可重復(fù)讀(Non-Repeatable Read):在解決臟讀問(wèn)題之后,能夠保證讀事務(wù)讀取到的數(shù)據(jù)都是持久的數(shù)據(jù),如果事務(wù)A多次讀取同一數(shù)據(jù),正好在兩次讀取之間,另外一個(gè)并行事務(wù)B提交了這一數(shù)據(jù)的修改,這就導(dǎo)致事務(wù)A多次讀取到的同一數(shù)據(jù)內(nèi)容不一樣。
- 幻讀(Phantom):與不可重復(fù)讀類(lèi)似,事務(wù)A多次查詢(xún)一個(gè)范圍,另外一個(gè)并行事務(wù)B向該范圍內(nèi)插入或刪除了數(shù)據(jù)并提交,當(dāng)事務(wù)A再次查詢(xún)時(shí)發(fā)現(xiàn)記錄變多或者丟失。
- 更新丟失(Lost Updates):兩個(gè)事務(wù)A和B修改了同一數(shù)據(jù),由于未提交事務(wù)之間看不到對(duì)方的修改,因此都以一個(gè)舊的前提去更新了同一數(shù)據(jù)。
- 寫(xiě)偏差(Write Skew):與更新丟失類(lèi)似,都是寫(xiě)前提被改變,寫(xiě)偏差則是事務(wù)A讀取某些數(shù)據(jù),作為另一些寫(xiě)入的前提條件(更新丟失是針對(duì)同一數(shù)據(jù)),但這時(shí)另外一個(gè)事務(wù)B對(duì)事務(wù)A已讀取的數(shù)據(jù)做了修改并提交,從而導(dǎo)致事務(wù)A做了錯(cuò)誤的commit操作。
- 讀偏差(Read Skew):如事務(wù)A讀取某兩個(gè)數(shù)據(jù)求和,事務(wù)B在事務(wù)A讀取期間對(duì)已讀取數(shù)據(jù)做了增減,此時(shí)事務(wù)A求和得到的結(jié)果就會(huì)與實(shí)際的結(jié)果不一致。
針對(duì)上面的并發(fā)問(wèn)題,InnoDB存儲(chǔ)引擎通過(guò)MVCC(當(dāng)然MVCC本質(zhì)上也是一種樂(lè)觀鎖)與鎖(關(guān)于鎖的介紹可以閱讀我的上一篇文章)來(lái)解決事務(wù)的隔離性一致性問(wèn)題。
事務(wù)隔離級(jí)別
事務(wù)隔離級(jí)別是MySQL對(duì)ACID的實(shí)現(xiàn)程度上的分級(jí),分為了四個(gè)等級(jí),等級(jí)越高數(shù)據(jù)庫(kù)越安全,每種隔離級(jí)別解決了不同事務(wù)并發(fā)一致性的問(wèn)題,具體如下:
- READ UNCOMMITTED(讀未提交):這是一個(gè)最差的隔離級(jí)別,該級(jí)別下事務(wù)可以讀到其它事務(wù)未提交的數(shù)據(jù),也就是說(shuō)在該事務(wù)隔離級(jí)別下會(huì)發(fā)生上述的所有并發(fā)一致性問(wèn)題。
- READ COMMITTED(讀已提交):事務(wù)只能讀取到已提交的修改,也就是說(shuō)多個(gè)并發(fā)的事務(wù)之間的修改是相互不可見(jiàn)的,該事務(wù)隔離級(jí)別解決了臟讀問(wèn)題。
- REPEATABLE READ(可重復(fù)讀):該級(jí)別保證同一個(gè)事務(wù)中多次讀取同一數(shù)據(jù)的結(jié)果是一致的,該級(jí)別是InnoDB默認(rèn)的隔離級(jí)別,該隔離級(jí)別解決了臟讀與不可重復(fù)讀問(wèn)題,但是仍可能出現(xiàn)幻讀的情況(InnoDB存儲(chǔ)引擎在該隔離級(jí)別下使用了Next-Key Lock解決了幻讀問(wèn)題)。
- SERIALIZABLE(串行化):強(qiáng)制事務(wù)串行化執(zhí)行,沒(méi)有并發(fā),那么并發(fā)問(wèn)題自然就不存在了,當(dāng)然在該級(jí)別下的事務(wù)性能非常低。
關(guān)于事務(wù)隔離的實(shí)現(xiàn)會(huì)在后續(xù)文章詳細(xì)講解,本文不在展開(kāi)。
事務(wù)的執(zhí)行過(guò)程
事務(wù)的執(zhí)行過(guò)程
- 查詢(xún)數(shù)據(jù),若數(shù)據(jù)不存在于buffer,則從磁盤(pán)加載;
- 數(shù)據(jù)更新前,先將當(dāng)前數(shù)據(jù)記錄到undo log重,以便后續(xù)可能出現(xiàn)的回滾做準(zhǔn)備;
- 更新Buffer Pool中的數(shù)據(jù);
- 將更新的數(shù)據(jù)寫(xiě)入到Redo Log Buffer中;
- 準(zhǔn)備提交事務(wù),調(diào)用fsync將Redo Log Buffer的數(shù)據(jù)寫(xiě)入到redo log文件中,狀態(tài)記為prepared;
- 準(zhǔn)備提交事務(wù),binlog寫(xiě)入到磁盤(pán)中;
- binlog寫(xiě)入成功后,將redo log的狀態(tài)更新為commit;
binlog的開(kāi)啟時(shí)會(huì)存在一個(gè)內(nèi)部XA的問(wèn)題(binlog是在MySQL層,而redo log在存儲(chǔ)引擎層),這里引入了2PC(二階段提交):
- prepare階段:redo log持久化到磁盤(pán),同時(shí)設(shè)置狀態(tài)為prepared,binlog此時(shí)不錯(cuò)任何操作。
- commit階段:存儲(chǔ)引擎釋放鎖,是否回滾段,然后binlog持久化到磁盤(pán),然后存儲(chǔ)引擎層提交,更改redo log的狀態(tài)為commit。
新聞標(biāo)題:MySQL事務(wù)詳解
網(wǎng)頁(yè)URL:http://www.fisionsoft.com.cn/article/coedpic.html


咨詢(xún)
建站咨詢(xún)
