1沼溜、ACID
事務(wù)是數(shù)據(jù)庫(kù)區(qū)別于文件系統(tǒng)的重要特性之一潮梯。
InnoDB的事務(wù)完全符合ACID特性骗灶。
- 原子性(Atomicity):操作過(guò)程不可分割,要么全部成功秉馏,要么全部失敗
- 一致性(Consistency):完整性約束不被破壞
- 隔離性(Isolution):事務(wù)提交前對(duì)其他事務(wù)不可見(jiàn)
- 持久性(Durability):事務(wù)一旦提交耙旦,其結(jié)果就是永久性的
In弄DB中的ACID特性依靠如下機(jī)制實(shí)現(xiàn):
- 隔離性由鎖來(lái)實(shí)現(xiàn);
- 原子性和持久性由redo log來(lái)實(shí)現(xiàn)萝究;
- 一致性由undo log來(lái)實(shí)現(xiàn)免都。
2、隔離級(jí)別
事務(wù)的隔離性是數(shù)據(jù)庫(kù)處理數(shù)據(jù)的幾大基礎(chǔ)之一帆竹,而隔離級(jí)別其實(shí)就是提供給用戶用于在性能和可靠性做出選擇和權(quán)衡的配置項(xiàng)绕娘。
InnoDB 遵循了 SQL:1992 標(biāo)準(zhǔn)中的四種隔離級(jí)別:
-
RAED UNCOMMITED
:使用查詢語(yǔ)句不會(huì)加鎖,可能會(huì)讀到未提交的行(Dirty Read)栽连; -
READ COMMITED
:只對(duì)記錄加記錄鎖险领,而不會(huì)在記錄之間加間隙鎖,所以允許新的記錄插入到被鎖定記錄的附近秒紧,所以再多次使用查詢語(yǔ)句時(shí)绢陌,可能得到不同的結(jié)果(Non-Repeatable Read); -
REPEATABLE READ
:多次讀取同一范圍的數(shù)據(jù)會(huì)返回第一次查詢的快照噩茄,不會(huì)返回不同的數(shù)據(jù)行下面,但是可能發(fā)生幻讀(Phantom Read); -
SERIALIZABLE
:InnoDB 隱式地將全部的查詢語(yǔ)句加上共享鎖绩聘,解決了幻讀的問(wèn)題沥割;
MySQL 中默認(rèn)的事務(wù)隔離級(jí)別就是 REPEATABLE READ
耗啦,但是它通過(guò) Next-Key
鎖也能夠在某種程度上解決幻讀的問(wèn)題。
接下來(lái)机杜,我們將數(shù)據(jù)庫(kù)中創(chuàng)建如下的表并通過(guò)個(gè)例子來(lái)展示在不同的事務(wù)隔離級(jí)別之下帜讲,會(huì)發(fā)生什么樣的問(wèn)題:
CREATE TABLE test(
id INT NOT NULL,
UNIQUE(id)
);
2.1 臟讀
當(dāng)事務(wù)的隔離級(jí)別為 READ UNCOMMITED
時(shí),我們?cè)?SESSION 2
中插入的未提交數(shù)據(jù)在 SESSION 1
中是可以訪問(wèn)的椒拗。
2.2 不可重復(fù)讀
當(dāng)事務(wù)的隔離級(jí)別為 READ COMMITED
時(shí)似将,雖然解決了臟讀的問(wèn)題,但是如果在 SESSION 1
先查詢了一個(gè)范圍的數(shù)據(jù)蚀苛,在這之后 SESSION 2
中插入一條數(shù)據(jù)并且提交了修改在验,在這時(shí),如果 SESSION 1
中再次使用相同的查詢語(yǔ)句堵未,就會(huì)發(fā)現(xiàn)兩次查詢的結(jié)果不一樣腋舌。
不可重復(fù)讀的原因就是,在 READ COMMITED
的隔離級(jí)別下渗蟹,存儲(chǔ)引擎不會(huì)在查詢記錄時(shí)添加間隙鎖块饺,鎖定 id < 5
這個(gè)范圍。
2.3 幻讀
重新開(kāi)啟了兩個(gè)會(huì)話 SESSION 1
和 SESSION 2
雌芽,在 SESSION 1
中我們查詢?nèi)淼男畔⑹诩瑁瑳](méi)有得到任何記錄;在 SESSION 2
中向表中插入一條數(shù)據(jù)并提交世落;由于 REPEATABLE READ
的原因淮腾,再次查詢?nèi)淼臄?shù)據(jù)時(shí),我們獲得到的仍然是空集岛心,但是在向表中插入同樣的數(shù)據(jù)卻出現(xiàn)了錯(cuò)誤来破。
這種現(xiàn)象在數(shù)據(jù)庫(kù)中就被稱作幻讀,雖然我們使用查詢語(yǔ)句得到了一個(gè)空的集合忘古,但是插入數(shù)據(jù)時(shí)卻得到了錯(cuò)誤徘禁,好像之前的查詢是幻覺(jué)一樣。
在標(biāo)準(zhǔn)的事務(wù)隔離級(jí)別中髓堪,幻讀是由更高的隔離級(jí)別 SERIALIZABLE
解決的送朱,但是它也可以通過(guò) MySQL 提供的 Next-Key 鎖解決:
REPERATABLE READ
和 READ UNCOMMITED
其實(shí)是矛盾的,如果保證了前者就看不到已經(jīng)提交的事務(wù)干旁,如果保證了后者驶沼,就會(huì)導(dǎo)致兩次查詢的結(jié)果不同,MySQL 為我們提供了一種折中的方式争群,能夠在 REPERATABLE READ
模式下加鎖訪問(wèn)已經(jīng)提交的數(shù)據(jù)回怜,其本身并不能解決幻讀的問(wèn)題,而是通過(guò)文章前面提到的 Next-Key 鎖來(lái)解決换薄。
3玉雾、重做日志(redo log)
3.1 write-ahead logging
預(yù)寫(xiě)式日志(write-ahead logging翔试,WAL ),是一種實(shí)現(xiàn)事務(wù)日志的標(biāo)準(zhǔn)方法复旬,其中心思想是對(duì)數(shù)據(jù)文件的修改必須發(fā)生在這些修改已經(jīng)記錄了日志之后垦缅。
如果遵循這個(gè)過(guò)程,那么就不需要在每次事務(wù)提交的時(shí)候都把數(shù)據(jù)頁(yè)沖刷到磁盤(pán)驹碍,因?yàn)榧幢愠霈F(xiàn)宕機(jī)的情況壁涎, 我們也可以用日志來(lái)恢復(fù)數(shù)據(jù)庫(kù):任何尚未附加到數(shù)據(jù)頁(yè)的記錄都將先從日志記錄中重做(這叫向前滾動(dòng)恢復(fù),也叫做 redo)志秃。
使用 WAL 的第一個(gè)主要的好處就是顯著地減少了磁盤(pán)寫(xiě)的次數(shù)怔球。 因?yàn)樵谌罩咎峤坏臅r(shí)候只有日志文件需要沖刷到磁盤(pán);而不是事務(wù)修改的所有數(shù)據(jù)文件洽损。 在多用戶環(huán)境里庞溜,許多事務(wù)的提交可以用日志文件的一次 fsync()
來(lái)完成。而且碑定,日志文件是順序?qū)懙?/strong>, 因此同步日志的開(kāi)銷要遠(yuǎn)比同步數(shù)據(jù)頁(yè)的開(kāi)銷要小又官。 這一點(diǎn)對(duì)于許多小事務(wù)對(duì)磁盤(pán)的隨機(jī)寫(xiě)入更是如此延刘。
3.2 基本概念
MySQL作為一個(gè)存儲(chǔ)系統(tǒng),為了保證數(shù)據(jù)的可靠性六敬,最終得落盤(pán)碘赖;又為了數(shù)據(jù)寫(xiě)入的速度,需要引入基于內(nèi)存的“緩沖池”外构。也就是說(shuō)普泡,數(shù)據(jù)是先緩存在緩沖池中,然后再以某種方式刷新到磁盤(pán)审编,在某個(gè)時(shí)間段之內(nèi)撼班,緩存中的數(shù)據(jù)與磁盤(pán)的數(shù)是不一致的,如果這個(gè)時(shí)候宕機(jī)會(huì)導(dǎo)致緩存數(shù)據(jù)的丟失垒酬。
引入redo log的目的就是為了防止以上問(wèn)題的發(fā)生砰嘁。
當(dāng)向InnoDB寫(xiě)用戶數(shù)據(jù)時(shí),先寫(xiě)redo log勘究,然后redo log根據(jù)一定的規(guī)則持久化到磁盤(pán)矮湘,變成redo log file,用戶數(shù)據(jù)則在buffer中(比如數(shù)據(jù)頁(yè)口糕、索引頁(yè))缅阳。如果發(fā)生宕機(jī),重啟后則讀取磁盤(pán)上的 redo log file 進(jìn)行數(shù)據(jù)的恢復(fù)景描。
從這個(gè)角度來(lái)說(shuō)十办,InnoDB事務(wù)的持久性是通過(guò) redo log 來(lái)實(shí)現(xiàn)的秀撇。
下面以一個(gè)更新事務(wù)為例,宏觀上把握redo log 流轉(zhuǎn)過(guò)程橘洞,如下圖所示:
- 第一步:先將原始數(shù)據(jù)從磁盤(pán)中讀入內(nèi)存中來(lái)捌袜,修改數(shù)據(jù)的內(nèi)存拷貝
- 第二步:生成一條重做日志并寫(xiě)入redo log buffer,記錄的是數(shù)據(jù)被修改后的值
- 第三步:當(dāng)事務(wù)commit時(shí)炸枣,將redo log buffer中的內(nèi)容刷新到 redo log file虏等,對(duì) redo log file采用追加寫(xiě)的方式
- 第四步:定期將內(nèi)存中修改的數(shù)據(jù)刷新到磁盤(pán)中
redo log包括兩部分:
- 內(nèi)存中的日志緩沖(redo log buffer),該部分日志是易失性的
- 磁盤(pán)上的重做日志文件(redo log file)适肠,該部分日志是持久的
InnoDB通過(guò)force log at commit機(jī)制實(shí)現(xiàn)事務(wù)的持久性霍衫,即在事務(wù)提交的時(shí)候,必須先將該事務(wù)的所有事務(wù)日志寫(xiě)入到磁盤(pán)上的redo log file和undo log file中進(jìn)行持久化侯养。
為了確保每次日志都能寫(xiě)入到事務(wù)日志文件中敦跌,在每次將log buffer中的日志寫(xiě)入日志文件的過(guò)程中都會(huì)調(diào)用一次操作系統(tǒng)的fsync操作(即fsync()系統(tǒng)調(diào)用)。因?yàn)镸ySQL是工作在用戶空間的逛揩,MySQL的log buffer處于用戶空間的內(nèi)存中柠傍。要寫(xiě)入到磁盤(pán)上的log file中,中間還要經(jīng)過(guò)操作系統(tǒng)內(nèi)核空間的os buffer辩稽,調(diào)用fsync()的作用就是將OS buffer中的日志刷到磁盤(pán)上的log file中惧笛。
也就是說(shuō),從redo log buffer寫(xiě)日志到磁盤(pán)的redo log file中逞泄,過(guò)程如下:
redo log buffer 刷新到磁盤(pán)的時(shí)機(jī)由參數(shù) InnoDB_flush_log_at_trx_commit
控制患整,默認(rèn)為1:
- 0: 事務(wù)提交時(shí)不進(jìn)行寫(xiě)入redo log操作,而是依靠master線程周期性的刷盤(pán)來(lái)保證喷众。由于master線程每1秒進(jìn)行一次redo log的fsync操作各谚,因此實(shí)例崩潰后最多丟失1秒鐘內(nèi)的事務(wù)
- 1: 事務(wù)提交時(shí)必須調(diào)用一次
fsync
操作,這種方式即使系統(tǒng)崩潰也不會(huì)丟失任何數(shù)據(jù)到千,但是因?yàn)槊看翁峤欢紝?xiě)入磁盤(pán)昌渤,IO的性能較差。該值是InnoDB的默認(rèn)值 - 2: 在事務(wù)提交時(shí)只保證將redo log buffer寫(xiě)到系統(tǒng)的os buffer中父阻,不進(jìn)行
fsync
操作愈涩,因此如果MySQL數(shù)據(jù)庫(kù)宕機(jī)時(shí) 不會(huì)丟失事務(wù),但操作系統(tǒng)宕機(jī)則可能丟失事務(wù)
3.3 兩次寫(xiě)
當(dāng)發(fā)生數(shù)據(jù)庫(kù)宕機(jī)時(shí)加矛,可能InnoDB存儲(chǔ)引擎正在寫(xiě)入某個(gè)頁(yè)到表中履婉,而這個(gè)頁(yè)只寫(xiě)了一部分,比如16KB的頁(yè)斟览,只寫(xiě)了前4KB毁腿,之后就發(fā)生了宕機(jī),這種情況被稱為部分寫(xiě)失效(partial page write)。
部分寫(xiě)失效的問(wèn)題已烤,依靠重做日志無(wú)法恢復(fù)鸠窗,因?yàn)橹刈鋈罩臼腔谄屏康奈锢聿僮鳌@缈杈浚瑢?xiě)'aaaa'記錄到偏移量800的位置稍计,如果這個(gè)頁(yè)本身已經(jīng)發(fā)生了損壞,再對(duì)其進(jìn)行重做是沒(méi)有意義的裕循。這就是說(shuō)臣嚣,在使用重做日志前,用戶需要一個(gè)頁(yè)的副本剥哑,當(dāng)寫(xiě)入失效發(fā)生時(shí)硅则,先通過(guò)頁(yè)的副本來(lái)還原該頁(yè),再進(jìn)行重做株婴,這就是doublewrite怎虫。
下面是兩次寫(xiě)的原理圖:
doublewrite由兩部分組成:
- 內(nèi)存中的doublewrite buffer,大小為2MB
- 物理磁盤(pán)上共享表空間中連續(xù)的128個(gè)頁(yè)困介,即2個(gè)區(qū)(extent)乔遮,大小同樣為2MB
當(dāng)刷新緩沖池臟頁(yè)時(shí)鲫凶,并不直接寫(xiě)到數(shù)據(jù)文件中鳞绕,而是按照下面的路程執(zhí)行:
- 拷貝至內(nèi)存中的兩次寫(xiě)緩沖區(qū)氧急。
- 從兩次寫(xiě)緩沖區(qū)分兩次寫(xiě)入磁盤(pán)共享表空間中,每次1MB
- 待第2步完成后八回,再將兩次寫(xiě)緩沖區(qū)寫(xiě)入數(shù)據(jù)文件
這樣就可以解決上文提到的部分寫(xiě)失效的問(wèn)題,因?yàn)樵诖疟P(pán)共享表空間中已有數(shù)據(jù)頁(yè)副本拷貝驾诈,如果數(shù)據(jù)庫(kù)在頁(yè)寫(xiě)入數(shù)據(jù)文件的過(guò)程中宕機(jī)缠诅,在實(shí)例恢復(fù)時(shí),可以從共享表空間中找到該頁(yè)副本乍迄,將其拷貝覆蓋原有的數(shù)據(jù)頁(yè)管引,再應(yīng)用重做日志即可。
其中第2步是額外的性能開(kāi)銷闯两,但由于磁盤(pán)共享表空間是連續(xù)的褥伴,因此開(kāi)銷不是很大⊙牵可以通過(guò)參數(shù)skip_innodb_doublewrite
禁用兩次寫(xiě)功能重慢,默認(rèn)是開(kāi)啟的。
3.4 LSN
緩存中未刷到磁盤(pán)的數(shù)據(jù)稱為臟數(shù)據(jù)(dirty data)逊躁。由于數(shù)據(jù)和日志都以頁(yè)的形式存在似踱,所以臟頁(yè)包含臟數(shù)據(jù)和臟日志。
在InnoDB中,由一系列的規(guī)則來(lái)判斷是否到達(dá)了checkpoint
核芽,到達(dá)后會(huì)將緩存中的臟數(shù)據(jù)頁(yè)和臟日志頁(yè)都刷到磁盤(pán)囚戚。
LSN
稱為日志的邏輯序列號(hào)(log sequence number),隨著日志的寫(xiě)入而逐漸增大轧简。根據(jù)LSN驰坊,可以獲取到幾個(gè)有用的信息:
- 數(shù)據(jù)頁(yè)的版本信息。
- 寫(xiě)入的日志總量哮独,通過(guò)LSN開(kāi)始號(hào)碼和結(jié)束號(hào)碼可以計(jì)算出寫(xiě)入的日志量拳芙。
- 可知道檢查點(diǎn)的位置。
可以通過(guò)show engine innodb status
命令查看LSN的情況:
mysql> show engine innodb status\G;
*************************** 1. row ***************************
...
---
LOG
---
Log sequence number 2611354
Log flushed up to 2611354
Pages flushed up to 2611354
Last checkpoint at 2611345
0 pending log flushes, 0 pending chkp writes
10 log i/o's done, 0.09 log i/o's/second
...
- Log sequence number表示當(dāng)前的LSN,
- Log flushed up to表示刷新到重做日志文件的LSN
- Pages flushed up to表示寫(xiě)入磁盤(pán)的dirty page 上的LSN
- Last checkpoint at表示寫(xiě)入磁盤(pán)的checkpoint上的LSN
3.5 恢復(fù)過(guò)程
在啟動(dòng)InnoDB的時(shí)候借嗽,不管上次是正常關(guān)閉還是異常關(guān)閉态鳖,總是會(huì)進(jìn)行恢復(fù)操作。
因?yàn)?strong>redo log記錄的是數(shù)據(jù)頁(yè)的物理變化恶导,因此恢復(fù)的時(shí)候速度比邏輯日志(如二進(jìn)制日志)要快很多浆竭。而且,InnoDB自身也做了一定程度的優(yōu)化惨寿,讓恢復(fù)速度變得更快邦泄。
重啟InnoDB時(shí),checkpoint表示已經(jīng)完整刷到磁盤(pán)上data page上的LSN裂垦,因此恢復(fù)時(shí)僅需要恢復(fù)從checkpoint開(kāi)始的日志部分顺囊。例如,當(dāng)數(shù)據(jù)庫(kù)在上一次checkpoint的LSN為10000時(shí)宕機(jī)蕉拢,且事務(wù)是已經(jīng)提交過(guò)的狀態(tài)特碳。啟動(dòng)數(shù)據(jù)庫(kù)時(shí)會(huì)檢查磁盤(pán)中數(shù)據(jù)頁(yè)的LSN,如果數(shù)據(jù)頁(yè)的LSN小于日志中的LSN晕换,則會(huì)從檢查點(diǎn)開(kāi)始恢復(fù)午乓。
還有一種情況,在宕機(jī)前正處于checkpoint的刷盤(pán)過(guò)程闸准,且數(shù)據(jù)頁(yè)的刷盤(pán)進(jìn)度超過(guò)了日志頁(yè)的刷盤(pán)進(jìn)度益愈。這時(shí)候一宕機(jī),數(shù)據(jù)頁(yè)中記錄的LSN就會(huì)大于日志頁(yè)中的LSN夷家,在重啟的恢復(fù)過(guò)程中會(huì)檢查到這一情況蒸其,這時(shí)超出日志進(jìn)度的部分將不會(huì)重做,因?yàn)檫@本身就表示已經(jīng)做過(guò)的事情库快,無(wú)需再重做摸袁。
另外,事務(wù)日志具有冪等性缺谴,所以多次操作得到同一結(jié)果的行為在日志中只記錄一次但惶。而二進(jìn)制日志不具有冪等性耳鸯,多次操作會(huì)全部記錄下來(lái),在恢復(fù)的時(shí)候會(huì)多次執(zhí)行二進(jìn)制日志中的記錄膀曾,速度就慢得多县爬。例如,某記錄中id初始值為2添谊,通過(guò)update將值設(shè)置為了3财喳,后來(lái)又設(shè)置成了2,在事務(wù)日志中記錄的將是無(wú)變化的頁(yè)斩狱,根本無(wú)需恢復(fù)耳高;而二進(jìn)制會(huì)記錄下兩次update操作,恢復(fù)時(shí)也將執(zhí)行這兩次update操作所踊,速度比事務(wù)日志恢復(fù)更慢泌枪。
4、回滾日志(undo log)
4.1 基本概念
重做日志記錄了事務(wù)的行為秕岛,可以很好地通過(guò)其進(jìn)行"重做"碌燕。但是事務(wù)有時(shí)還需要撤銷,這是就需要undo继薛。undo與redo正好相反修壕,對(duì)于數(shù)據(jù)庫(kù)進(jìn)行修改時(shí),數(shù)據(jù)庫(kù)不但會(huì)產(chǎn)生redo遏考,而且還會(huì)產(chǎn)生一定量的undo慈鸠。如果因?yàn)槟承┰驅(qū)е率聞?wù)失敗或回滾了,可以借助該undo進(jìn)行回滾灌具。
undo log有兩個(gè)作用:
-
提供回滾
當(dāng)事務(wù)回滾時(shí)青团,或者數(shù)據(jù)庫(kù)崩潰后重啟,可以利用undo日志咖楣,即舊版本數(shù)據(jù)壶冒,撤銷未提交事務(wù)對(duì)數(shù)據(jù)庫(kù)產(chǎn)生的影響。
-
多版本控制(MVCC)
當(dāng)讀取的某一行被其他事務(wù)鎖定時(shí)截歉,它可以從undo log中分析出該行記錄以前的數(shù)據(jù)是什么,從而提供該行版本信息烟零,讓用戶實(shí)現(xiàn)非鎖定一致性讀取瘪松。
與redo不同的是,redo存放在重做日志文件中锨阿,undo存放在數(shù)據(jù)庫(kù)內(nèi)部的一個(gè)特殊的段(segment)中宵睦,稱為undo段(undo segment),undo段位于共享表空間內(nèi)墅诡。InnoDB對(duì)undo的管理同樣采用段的方式壳嚎,稱為rollback segment。每個(gè)回滾段記錄了1024個(gè)undo段。
undo log和redo log記錄物理日志不一樣烟馅,它是邏輯日志说庭。可以認(rèn)為當(dāng)delete一條記錄時(shí)郑趁,undo log中會(huì)記錄一條對(duì)應(yīng)的insert記錄刊驴,反之亦然,當(dāng)update一條記錄時(shí)寡润,它記錄一條對(duì)應(yīng)相反的update記錄捆憎。
另外,undo log也會(huì)產(chǎn)生redo log梭纹,因?yàn)閡ndo log也要實(shí)現(xiàn)持久性保護(hù)躲惰。
4.2 purge
purge線程兩個(gè)主要作用是:清理undo頁(yè)和清除page里面帶有Delete_Bit標(biāo)識(shí)的數(shù)據(jù)行。
對(duì)插入变抽、更新础拨、刪除操作來(lái)說(shuō),purge操作的作用如下:
-
insert
因?yàn)閕nsert操作的記錄瞬沦,只對(duì)事務(wù)本身可見(jiàn)太伊,對(duì)其他事務(wù)不可見(jiàn),所以不需要保證MVCC逛钻。故該undo log可以在事務(wù)提交后直接刪除僚焦,不需要進(jìn)行purge操作。
-
update
該undo log可能需要服務(wù)MVCC曙痘,因此不能再事務(wù)提交時(shí)就進(jìn)行刪除芳悲。提交時(shí)放入undo log鏈表,等待purge線程進(jìn)行最后的刪除
-
delete
與update類似边坤,delete的undo log也可能需要服務(wù)MVCC名扛。在InnoDB中,事務(wù)中的delete操作實(shí)際上并不是真正的刪除掉數(shù)據(jù)行茧痒,而是做了個(gè)標(biāo)記肮韧,真正的刪除工作需要后臺(tái)purge線程去完成。
5旺订、分布式事務(wù)
InnoDB通過(guò)XA事務(wù)來(lái)支持分布式事務(wù)的實(shí)現(xiàn)弄企。
XA事務(wù)由一個(gè)或多個(gè)資源管理器(Resource Manager)、一個(gè)事務(wù)管理器(Transaction Manager)以及一個(gè)應(yīng)用程序組成:
- 資源管理器:提供訪問(wèn)事務(wù)資源的方法区拳。通常一個(gè)數(shù)據(jù)就是一個(gè)資源管理器
- 事務(wù)管理器:協(xié)調(diào)參與全局事務(wù)中的各個(gè)事務(wù)拘领。需要和參與全局事務(wù)的所有資源管理器進(jìn)行通信
- 應(yīng)用程序:定義事務(wù)的邊界,指定全局事務(wù)中的操作
分布式事務(wù)使用兩段式提交(two-phase commit)的方式樱调。
- 第一個(gè)階段约素,所有參與全局事務(wù)的節(jié)點(diǎn)都開(kāi)始準(zhǔn)備届良,告訴事務(wù)管理器它們準(zhǔn)備好提交了。
- 第二個(gè)階段圣猎,事務(wù)管理器告訴資源管理器執(zhí)行rollback或者commit士葫,如果任何一個(gè)節(jié)點(diǎn)顯示不能commit,那么所有的節(jié)點(diǎn)就得全部rollback样漆。
在MySQL中還存在一種內(nèi)部XA事務(wù)为障,存在于存儲(chǔ)引擎與插件之間,又或者存儲(chǔ)引擎與存儲(chǔ)引擎之間放祟。
最常見(jiàn)的內(nèi)部XA事務(wù)是binlog與InnoDB之間鳍怨。 由于復(fù)制的需要,因此目前絕大多數(shù)的數(shù)據(jù)庫(kù)都開(kāi)啟了 binlog 功能跪妥。在事務(wù)提交時(shí)鞋喇,先寫(xiě)二進(jìn)制日志,再寫(xiě) InnoDB 存儲(chǔ)引擎的重做日志眉撵。對(duì)上述兩個(gè)操作的要求也是原子的侦香,即二進(jìn)制日志和重做日志必須同時(shí)寫(xiě)入。若二進(jìn)制日志先寫(xiě)了纽疟,而在寫(xiě)入 InnoDB 存儲(chǔ)引擎時(shí)發(fā)生了者機(jī)罐韩,那么 slave 可能會(huì)接收到 master 傳過(guò)去的二進(jìn)制日志并執(zhí)行,最終導(dǎo)致了主從不一致的情況污朽。
在上圖中散吵,如果執(zhí)行完第二步,未執(zhí)行第三步時(shí) MySQL 數(shù)據(jù)庫(kù)發(fā)生了宕機(jī) 蟆肆,則會(huì)發(fā)生主從不一致的情況矾睦。為了解決這個(gè)問(wèn)題,MySQL 數(shù)據(jù)庫(kù)在 binlog 與 InnoDB 存儲(chǔ)引擎之間采用XA事務(wù)炎功。當(dāng)事務(wù)提交時(shí)枚冗,InnoDB 存儲(chǔ)引擎會(huì)先做一個(gè)PREPARE操作,將事務(wù)的xid寫(xiě)入蛇损,接著進(jìn)行二進(jìn)制日志的寫(xiě)入赁温。
如果在 InnoDB 存儲(chǔ)引擎提交前,MySQL 數(shù)據(jù)庫(kù)宕機(jī)了淤齐,那么 MySQL 數(shù)據(jù)庫(kù)在重啟后會(huì)先檢查準(zhǔn)備的 UXID 事務(wù)是否已經(jīng)提交束世,若沒(méi)有,則在存儲(chǔ)引擎層再進(jìn)行一次提交操作床玻。