本節咱們主要介紹一下在存儲引擎層上是如何會丟數據的。mysql
InnoDB支持事務,同Oracle相似,事務提交須要寫redo、undo。採用日誌先行的策略,將數據的變動在內存中完成,而且將事務記錄成redo,順序的寫入redo日誌中,即表示該事務已經完成,就能夠返回給客戶已提交的信息。可是實際上被更改的數據還在內存中,並無刷新到磁盤,即尚未落地,當達到必定的條件,會觸發checkpoint,將內存中的數據(page)合併寫入到磁盤,這樣就減小了離散寫、IOPS,提升性能。sql
在這個過程當中,若是服務器宕機了,內存中的數據丟失,當重啓後,會經過redo日誌進行recovery重作。確保不會丟失數據。所以只要redo可以實時的寫入到磁盤,InnoDB就不會丟數據。數據庫
先來看一下innodb_flush_log_at_trx_commit這個參數:緩存
= 0 :每秒 write cache & flush disk安全
= 1 :每次commit都 write cache & flush disk服務器
= 2 :每次commit都 write cache,而後根據innodb_flush_log_at_timeout(默認爲1s)時間 flush disk網絡
從這三個配置來看,顯然innodb_flush_log_at_trx_commit=1最爲安全,由於每次commit都保證redo寫入了disk。可是這種方式性能對DML性能來講比較低,在咱們的測試中發現,若是設置爲2,DML性能要比設置爲1高10倍左右。架構
爲何oracle的實時寫要比innodb的實時寫性能更好?線程與進程?後面還須要研究oracle
你們能夠考慮一下0與2的區別?異步
在某些DML操做頻繁的場景下,庫的innodb_flush_log_at_trx_commit須要設置爲2,這樣就存在丟數據的風險:當服務器出現宕機,重啓後進行crash recovery則會丟失innodb_flush_log_at_timeout秒內的數據。
PS:當開啓了內部XA事務(默認開啓),且開啓binlog,狀況稍有不同,後面會進行介紹。
MyISAM存儲引擎在咱們的生產中用的並很少,可是系統的數據字典表元數據等都是存儲在MyISAM引擎下。
MyISAM不支持事務,且沒有data cache,全部DML操做只寫到OS cache中,flush disk操做均由OS來完成,所以若是服務器宕機,則這部分數據確定會丟失。
主從複製原理:MySQL主庫在事務提交時寫binlog,並經過sync_binlog參數來控制binlog刷新到磁盤「落地」,而備庫經過IO線程從主庫讀取binlog,並記錄到本地的relay log中,由本地的SQL線程再將relay log的數據應用到本地數據庫,以下圖所示:
從上圖咱們能夠看到,在主從環境中,增長了binlog,這就增長了環境的複雜性,所以也增長了丟數據以及數據不一致可能。
在分析這些丟數據的可能性以前,咱們先了解一下binlog的刷新機制以及MySQL的內部XA事務是如何保證binlog與redo的一致性的。
master寫binlog與innodb引擎寫redo相似,也有參數控制:sync_binlog
= 0 :表示MySQL不控制binlog的刷新,由文件系統本身控制它的緩存的刷新
> 0 :表示每sync_binlog次事務提交,MySQL調用文件系統的刷新操做將緩存刷下去
其中最安全的就是=1,表示每次事務提交,MySQL都會把binlog緩存刷下去,這樣在掉電等狀況下,系統纔有可能丟失1個事務的數據。當sync_binlog設置爲1,對系統的IO消耗也是很是大的。
MySQL的存儲引擎與MySQL服務層之間,或者存儲引擎與存儲引擎之間的分佈式事務,稱之爲內部XA事務。最爲常見的內部XA事務存在與binlog與InnoDB存儲引擎之間。在事務提交時,先寫二進制日誌,再寫InnoDB存儲引發的redo日誌。對於這個操做要求必須是原子的,即須要保證二者同時寫入,內部XA事務機制就是保證二者的同時寫入。
XA事務的大體流程:
事務提交後,InnoDB存儲引擎會先作一個PREPARE操做,將事務的XID寫入到redo日誌中。
寫binlog日誌
再將該事務的commit信息寫到redo log中
若是在步驟1和步驟2失敗的狀況下,整個事務會回滾,若是在步驟3失敗的狀況下,MySQL數據庫在重啓後會先檢查準備的UXID事務是否已經提交,若沒有,則在存儲引擎層再進行一次提交操做。這樣就保證了redo與binlog的一致性,防止丟數據。
上面咱們介紹了MySQL的內部XA事務流程,可是這個流程並非完美無缺的,redo的ib_logfile與binlog日誌若是被設置非實時flush,就有可能存在丟數據的狀況。
1.redo的trx_prepare未寫入,但binlog寫入,形成從庫數據量比主庫多。
2.redo的trx_prepare與commit都寫入了,可是binlog未寫入,形成從庫數據量比主庫少。
從目前來看,只能犧牲性能去換取數據的安全性,必需要設置redo和binlog爲實時刷盤,若是對性能要求很高,則考慮使用SSD
master正常,可是slave出現異常的狀況下宕機,這個時候會出現什麼樣的狀況呢?若是數據丟失,slave的SQL線程還會從新應用嗎?這個咱們須要先了解SQL線程的機制。
slave讀取master的binlog日誌後,須要落地3個文件:relay log、relay log info、master info:
relay log:即讀取過來的master的binlog,內容與格式與master的binlog一致
relay log info:記錄SQL Thread應用的relay log的位置、文件號等信息
master info:記錄IO Thread讀取master的binlog的位置、文件號、延遲等信息
所以若是當這3個文件若是不及時落地,則主機crash後會致使數據的不一致。
在MySQL 5.6.2以前,slave記錄的master信息以及slave應用binlog的信息存放在文件中,即master.info與relay-log.info。在5.6.2版本以後,容許記錄到table中,參數設置以下:
master-info-repository = TABLE relay-log-info-repository = TABLE
對應的表分別爲mysql.slave_master_info與mysql.slave_relay_log_info,且這兩個表均爲innodb引擎表。
master info與relay info還有3個參數控制刷新:
sync_relay_log:默認爲10000,即每10000次sync_relay_log事件會刷新到磁盤。爲0則表示不刷新,交由OS的cache控制。
sync_master_info:若master-info-repository爲FILE,當設置爲0,則每次sync_master_info事件都會刷新到磁盤,默認爲10000次刷新到磁盤;若master-info-repository爲TABLE,當設置爲0,則表不作任何更新,設置爲1,則每次事件會更新表 #默認爲10000
sync_relay_log_info:若relay_log_info_repository爲FILE,當設置爲0,交由OS刷新磁盤,默認爲10000次刷新到磁盤;若relay_log_info_repository爲TABLE,且爲INNODB存儲,則不管爲任何值,則都每次evnet都會更新表。
建議參數設置以下:
sync_relay_log = 1 sync_master_info = 1 sync_relay_log_info = 1 master-info-repository = TABLE relay-log-info-repository = TABLE
當這樣設置,致使調用fsync()/fdatasync()隨着master的事務的增長而增長,且若slave的binlog和redo也實時刷新的話,會帶來很嚴重的IO性能瓶頸。
當master出現故障後,binlog未及時傳到slave,或者各個slave收到的binlog不一致。且master沒法在第一時間恢復,這個時候怎麼辦?
若是master不切換,則整個數據庫只能只讀,影響應用的運行。
若是將別的slave提高爲新的master,那麼原master將來得及傳到slave的binlog的數據則會丟失,而且還涉及到下面2個問題。
1.各個slave之間接收到的binlog不一致,若是強制拉起一個slave,則slave之間數據會不一致。
2.原master恢復正常後,因爲新的master日誌丟棄了部分原master的binlog日誌,這些多出來的binlog日誌怎麼處理,從新搭建環境?
對於上面出現的問題,一種方法是確保binlog傳到從庫,或者說保證主庫的binlog有多個拷貝。第二種方法就是容許數據丟失,制定必定的策略,保證最小化丟失數據。
1.確保binlog所有傳到從庫
方案一:使用semi sync(半同步)方式,事務提交後,必需要傳到slave,事務才能算結束。對性能影響很大,依賴網絡適合小tps系統。
方案二:雙寫binlog,經過DBDR OS層的文件系統複製到備機,或者使用共享盤保存binlog日誌。
方案三:在數據層作文章,好比保證數據庫寫成功後,再異步隊列的方式寫一份,部分業務能夠藉助設計和數據流解決。
2.保證數據最小化丟失
上面的方案設計及架構比較複雜,若是能容忍數據的丟失,能夠考慮使用淘寶的TMHA複製管理工具。
當master宕機後,TMHA會選擇一個binlog接收最大的slave做爲master。當原master宕機恢復後,經過binlog的逆向應用,把原master上多執行的事務回退掉。
經過上面的總結分析,MySQL丟數據的場景是五花八門,涉及到單庫的丟數據場景、主從的丟數據場景以及MySQL內部XA事務原理等,相對還比較複雜,有點難以理解。
只有當咱們瞭解了這些丟數據的場景,才能更好的去學習, 並解決這些問題。
根據分佈式領域的CAP理論(Consistency、Availability、Partition tolerance),任何的分佈式系統只能同時知足2點,沒辦法三者兼顧。MySQL的主從環境知足Availability,且主從互不干擾,所以知足Partition tolerance,可是不知足Consistency,若是須要知足Consistency,則確定會失去Partition tolerance,所以實現100%高可用性的MySQL主從架構仍是很是困難的。只能經過一些設計去犧牲部分特性去知足另外的特性。