MySQL單機(jī)的數(shù)據(jù)一致性
MySQL作為一個(gè)可插拔的數(shù)據(jù)庫(kù)系統(tǒng)笼痛,支持插件式的存儲(chǔ)引擎,在設(shè)計(jì)上分為Server層和Storage Engine層琅拌。
在Server層缨伊,MySQL以events的形式記錄數(shù)據(jù)庫(kù)各種操作的Binlog二進(jìn)制日志,其基本核心作用有:復(fù)制和備份进宝。
除此之外刻坊,我們結(jié)合多樣化的業(yè)務(wù)場(chǎng)景需求,基于Binlog的特性構(gòu)建了強(qiáng)大的MySQL生態(tài)党晋,如:DTS谭胚、單元化、異構(gòu)系統(tǒng)之間實(shí)時(shí)同步等等未玻,Binlog早已成為MySQL生態(tài)中不可缺少的模塊灾而。
而在Storage Engine層,InnoDB作為比較通用的存儲(chǔ)引擎扳剿,其在高可用和高性能兩方面作了較好的平衡旁趟,早已經(jīng)成為使用MySQL的首選。
和大多數(shù)關(guān)系型數(shù)據(jù)庫(kù)一樣庇绽,InnoDB采用WAL技術(shù)轻庆,即InnoDB Redo Log記錄了對(duì)數(shù)據(jù)文件的物理更改癣猾,并保證總是日志先行敛劝,在持久化數(shù)據(jù)文件前余爆,保證之前的redo日志已經(jīng)寫到磁盤。
Binlog和InnoDB Redo Log是否落盤將直接影響實(shí)例在異常宕機(jī)后數(shù)據(jù)能恢復(fù)到什么程度夸盟。InnoDB提供了相應(yīng)的參數(shù)來(lái)控制事務(wù)提交時(shí)蛾方,寫日志的方式和策略,例如:
innodb_flush_method:控制innodb數(shù)據(jù)文件上陕、日志文件的打開(kāi)和刷寫的方式桩砰,建議取值:fsync、O_DIRECT释簿。
innodb_flush_log_at_trx_commit:控制每次事務(wù)提交時(shí)亚隅,重做日志的寫盤和落盤策略,可取值:0庶溶,1煮纵,2。
當(dāng)innodb_flush_log_at_trx_commit=1時(shí)偏螺,每次事務(wù)提交行疏,日志寫到InnoDB Log Buffer后,會(huì)等待Log Buffer中的日志寫到Innodb日志文件并刷新到磁盤上才返回成功套像。
sync_binlog:控制每次事務(wù)提交時(shí)酿联,Binlog日志多久刷新到磁盤上,可取值:0或者n(N為正整數(shù))夺巩。
不同取值會(huì)影響MySQL的性能和異常crash后數(shù)據(jù)能恢復(fù)的程度贞让。當(dāng)sync_binlog=1時(shí),MySQL每次事務(wù)提交都會(huì)將binlog_cache中的數(shù)據(jù)強(qiáng)制寫入磁盤柳譬。
innodb_doublewrite:控制是否打開(kāi)double writer功能喳张,取值ON或者OFF。
當(dāng)Innodb的page size默認(rèn)16K征绎,磁盤單次寫的page大小通常為4K或者遠(yuǎn)小于Innodb的page大小時(shí)蹲姐,發(fā)生了系統(tǒng)斷電/os crash ,剛好只有一部分寫是成功的人柿,則會(huì)遇到partial page write問(wèn)題柴墩,從而可能導(dǎo)致crash后由于部分寫失敗的page影響數(shù)據(jù)的恢復(fù)。InnoDB為此提供了Double Writer技術(shù)來(lái)避免partial page write的發(fā)生凫岖。
innodb_support_xa:控制是否開(kāi)啟InnoDB的兩階段事務(wù)提交.默認(rèn)情況下江咳,innodb_support_xa=true,支持xa兩段式事務(wù)提交哥放。
以上參數(shù)不同的取值分別影響著MySQL異常crash后數(shù)據(jù)能恢復(fù)的程度和寫入性能歼指,實(shí)際使用過(guò)程中爹土,需要結(jié)合業(yè)務(wù)的特性和實(shí)際需求,來(lái)設(shè)置合理的配置踩身。比如:
MySQL單實(shí)例胀茵,Binlog關(guān)閉場(chǎng)景:
innodb_flush_log_at_trx_commit=1,innodb_doublewrite=ON時(shí)挟阻,能夠保證不論是MySQL Crash 還是OS Crash 或者是主機(jī)斷電重啟都不會(huì)丟失數(shù)據(jù)琼娘。
MySQL單實(shí)例,Binlog開(kāi)啟場(chǎng)景:
默認(rèn)innodb_support_xa=ON附鸽,開(kāi)啟binlog后事務(wù)提交流程會(huì)變成兩階段提交脱拼,這里的兩階段提交并不涉及分布式事務(wù),mysql把它稱之為內(nèi)部xa事務(wù)坷备。
但是熄浓,當(dāng)由于主機(jī)硬件故障等原因?qū)е轮鳈C(jī)完全無(wú)法啟動(dòng)時(shí),則MySQL單實(shí)例面臨著單點(diǎn)故障導(dǎo)致數(shù)據(jù)丟失的風(fēng)險(xiǎn)省撑,故MySQL單實(shí)例通常不適用于生產(chǎn)環(huán)境赌蔑。
MySQL集群的數(shù)據(jù)一致性
MySQL集群通常指MySQL的主從復(fù)制架構(gòu)。
通常使用MySQL主從復(fù)制來(lái)解決MySQL的單點(diǎn)故障問(wèn)題丁侄,其通過(guò)邏輯復(fù)制的方式把主庫(kù)的變更同步到從庫(kù)惯雳,主備之間無(wú)法保證嚴(yán)格一致的模式,于是鸿摇,MySQL的主從復(fù)制帶來(lái)了主從“數(shù)據(jù)一致性”的問(wèn)題石景。MySQL的復(fù)制分為:異步復(fù)制、半同步復(fù)制拙吉、全同步復(fù)制潮孽。
異步復(fù)制
主庫(kù)在執(zhí)行完客戶端提交的事務(wù)后會(huì)立即將結(jié)果返給給客戶端,并不關(guān)心從庫(kù)是否已經(jīng)接收并處理筷黔,這樣就會(huì)有一個(gè)問(wèn)題往史,主如果crash掉了,此時(shí)主上已經(jīng)提交的事務(wù)可能并沒(méi)有傳到從庫(kù)上佛舱,如果此時(shí)椎例,強(qiáng)行將從提升為主,可能導(dǎo)致“數(shù)據(jù)不一致”请祖。早期MySQL僅僅支持異步復(fù)制订歪。
半同步復(fù)制
MySQL在5.5中引入了半同步復(fù)制,主庫(kù)在應(yīng)答客戶端提交的事務(wù)前需要保證至少一個(gè)從庫(kù)接收并寫到relay log中肆捕,半同步復(fù)制通過(guò)rpl_semi_sync_master_wait_point參數(shù)來(lái)控制master在哪個(gè)環(huán)節(jié)接收 slave ack刷晋,master 接收到 ack 后返回狀態(tài)給客戶端,此參數(shù)一共有兩個(gè)選項(xiàng) AFTER_SYNC & AFTER_COMMIT。
配置為WAIT_AFTER_COMMIT(圖片來(lái)自網(wǎng)絡(luò)):
rpl_semi_sync_master_wait_point為WAIT_AFTER_COMMIT時(shí)眼虱,commitTrx的調(diào)用在engine層commit之后喻奥,如上圖所示。
即在等待Slave ACK時(shí)候捏悬,雖然沒(méi)有返回當(dāng)前客戶端撞蚕,但事務(wù)已經(jīng)提交,其他客戶端會(huì)讀取到已提交事務(wù)邮破。如果Slave端還沒(méi)有讀到該事務(wù)的events诈豌,同時(shí)主庫(kù)發(fā)生了crash,然后切換到備庫(kù)抒和。
那么之前讀到的事務(wù)就不見(jiàn)了,出現(xiàn)了數(shù)據(jù)不一致的問(wèn)題彤蔽,如下圖所示(圖片來(lái)自網(wǎng)絡(luò))摧莽。
如果主庫(kù)永遠(yuǎn)啟動(dòng)不了,那么實(shí)際上在主庫(kù)已經(jīng)成功提交的事務(wù)顿痪,在從庫(kù)上是找不到的镊辕,也就是數(shù)據(jù)丟失了。
配置為WAIT_AFTER_SYNC
MySQL官方針對(duì)上述問(wèn)題蚁袭,在5.7.2引入了Loss-less Semi-Synchronous征懈,在調(diào)用binlog sync之后,engine層commit之前等待Slave ACK揩悄。這樣只有在確認(rèn)Slave收到事務(wù)events后卖哎,事務(wù)才會(huì)提交。
在after_sync模式下解決了after_commit模式帶來(lái)的數(shù)據(jù)不一致的問(wèn)題删性,因?yàn)橹鲙?kù)沒(méi)有提交事務(wù)亏娜。
但也會(huì)有個(gè)問(wèn)題,當(dāng)主庫(kù)在binlog flush并且binlog同步到了備庫(kù)之后蹬挺,binlog sync之前發(fā)生了abort维贺,那么很明顯這個(gè)事務(wù)在主庫(kù)上是未提交成功的(由于abort之前binlog未sync完成,主庫(kù)恢復(fù)后事務(wù)會(huì)被回滾掉)巴帮,但由于從庫(kù)已經(jīng)收到了這些Binlog溯泣,并且執(zhí)行成功,相當(dāng)于在從庫(kù)上多出了數(shù)據(jù)榕茧,從而可能造成“數(shù)據(jù)不一致”垃沦。
此外,MySQL半同步復(fù)制架構(gòu)中雪猪,主庫(kù)在等待備庫(kù)ack時(shí)候栏尚,如果超時(shí)會(huì)退化為異步后,也可能導(dǎo)致“數(shù)據(jù)不一致”。