掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
有個(gè)水友提問:

創(chuàng)新互聯(lián)專注于文水企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,成都商城網(wǎng)站開發(fā)。文水網(wǎng)站建設(shè)公司,為文水等地區(qū)提供建站服務(wù)。全流程定制開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
沈老師,我們有一次MySQL崩潰,重啟后發(fā)現(xiàn)有些已經(jīng)提交的事務(wù)對(duì)數(shù)據(jù)的修改丟失了,不是說事務(wù)能保證ACID特性么,想問下什么情況下可能導(dǎo)致“事務(wù)已經(jīng)提交,數(shù)據(jù)卻丟失”呢?
這個(gè)問題有點(diǎn)復(fù)雜,得先從redo log說起。
事務(wù)提交后,必須將事務(wù)對(duì)數(shù)據(jù)頁的修改刷(fsync)到磁盤上,才能保證事務(wù)的ACID特性。
這個(gè)刷盤,是一個(gè)隨機(jī)寫,隨機(jī)寫性能較低,如果每次事務(wù)提交都刷盤,會(huì)極大影響數(shù)據(jù)庫(kù)的性能。
架構(gòu)設(shè)計(jì)中有兩個(gè)常見的優(yōu)化方法:
這兩個(gè)優(yōu)化,數(shù)據(jù)庫(kù)都用上了。
先說第一個(gè)優(yōu)化,將對(duì)數(shù)據(jù)的修改先順序?qū)懙饺罩纠铮@個(gè)日志就是redo log。
假如某一時(shí)刻,數(shù)據(jù)庫(kù)崩潰,還沒來得及將數(shù)據(jù)頁刷盤,數(shù)據(jù)庫(kù)重啟時(shí),會(huì)重做redo log里的內(nèi)容,以保證已提交事務(wù)對(duì)數(shù)據(jù)的影響被刷到磁盤上。
一句話,redo log是為了保證已提交事務(wù)的ACID特性,同時(shí)能夠提高數(shù)據(jù)庫(kù)性能的技術(shù)。
既然redo log能保證事務(wù)的ACID特性,那為什么還會(huì)出現(xiàn),水友提問中出現(xiàn)的“數(shù)據(jù)庫(kù)崩潰,丟數(shù)據(jù)”的問題呢?一起看下redo log的實(shí)現(xiàn)細(xì)節(jié)。
畫了一個(gè)丑圖,簡(jiǎn)單說明下redo log的三層架構(gòu):
首先,事務(wù)提交的時(shí)候,會(huì)寫入Log Buffer,這里調(diào)用的是MySQL自己的函數(shù)WriteRedoLog;
接著,只有當(dāng)MySQL發(fā)起系統(tǒng)調(diào)用寫文件write時(shí),Log Buffer里的數(shù)據(jù),才會(huì)寫到OS cache。注意,MySQL系統(tǒng)調(diào)用完write之后,就認(rèn)為文件已經(jīng)寫完,如果不flush,什么時(shí)候落盤,是操作系統(tǒng)決定的;
畫外音:有時(shí)候打日志,明明printf了,tail -f卻看不到,就是這個(gè)原因,操作系統(tǒng)還沒有刷盤。
最后,由操作系統(tǒng)(當(dāng)然,MySQL也可以主動(dòng)flush)將OS cache里的數(shù)據(jù),最終fsync到磁盤上。
這里就是將“每次寫”優(yōu)化為“批量寫”,以提高操作系統(tǒng)性能。
這也是“每次寫”優(yōu)化為“批量寫”思路的體現(xiàn),以提高數(shù)據(jù)庫(kù)性能。
畫外音:這個(gè)優(yōu)化思路,非常常見,高并發(fā)的MQ落盤,高并發(fā)的業(yè)務(wù)數(shù)據(jù)落盤,都可以使用。 redo log的三層架構(gòu),MySQL做了一次批量寫優(yōu)化,OS做了一次批量寫優(yōu)化,確實(shí)能極大提升性能,但有什么副作用嗎?
畫外音:有優(yōu)點(diǎn),必有缺點(diǎn)。
這個(gè)副作用,就是可能丟失數(shù)據(jù):
畫外音:如上文所說,應(yīng)用程序系統(tǒng)調(diào)用完write之后(不可能每次write后都立刻flush,這樣寫日志很蠢),就認(rèn)為寫成功了,操作系統(tǒng)何時(shí)fsync,應(yīng)用程序并不知道,如果操作系統(tǒng)崩潰,數(shù)據(jù)可能丟失。
任何脫離業(yè)務(wù)的技術(shù)方案都是耍流氓:
MySQL有一個(gè)參數(shù):
innodb_flush_log_at_trx_commit
能夠控制事務(wù)提交時(shí),刷redo log的策略。
目前有三種策略:
策略一:最佳性能(innodb_flush_log_at_trx_commit=0)
策略二:強(qiáng)一致(innodb_flush_log_at_trx_commit=1)
策略三:折衷(innodb_flush_log_at_trx_commit=2)
畫外音:磁盤IO次數(shù)不確定,因?yàn)椴僮飨到y(tǒng)的fsync頻率并不是MySQL能控制的。
畫外音:因?yàn)镺S也會(huì)fsync,MySQL主動(dòng)fsync的周期是一秒,所以最多丟一秒數(shù)據(jù)。
講了這么多,回到水友的提問上來,數(shù)據(jù)庫(kù)崩潰,重啟后丟失了數(shù)據(jù),有很大的可能,是將innodb_flush_log_at_trx_commit參數(shù)設(shè)置為0了,這位水友最好和DBA一起檢查一下InnoDB的配置。
高并發(fā)業(yè)務(wù),行業(yè)最佳實(shí)踐,是使用第三種折衷配置(=2),這是因?yàn)椋?/p>
(1) 為了保證事務(wù)的ACID特性,理論上每次事務(wù)提交都應(yīng)該刷盤,但此時(shí)效率很低,有兩種優(yōu)化方向:
(2) redo log是一種順序?qū)?,它有三層架?gòu):
(3) 為了滿足不同業(yè)務(wù)對(duì)于吞吐量與一致性的需求,MySQL事務(wù)提交時(shí)刷redo log有三種策略:
(4) 高并發(fā)業(yè)務(wù),行業(yè)內(nèi)的最佳實(shí)踐,是:
innodb_flush_log_at_trx_commit=2
知其然,知其所以然,希望大家有收獲。

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流