一、存儲引擎層面丟失數據
由於在實際項目中,我們往往使用支持事務的InnoDB存儲引擎。我們分析InnoDB存儲引擎數據丟失:
從上篇的文章《MySQL事務提交過程(一)》和《MySQL事務提交過程(二)》中知道,MySQL默認情況下是開啟內部的XA事務和事務的實現方式是基於redo log和undo log。也可以理解為MySQL事務是采用日志現行的策略。前提未開啟binlog的情況下,數據的變更首先在內存中完成,並且將事務順序的寫入到redo log中,即表示該事務已經完成,就可以返回發給客戶端已提交的信息。但此時變更后的數據還在內存中,並沒有刷新寫入到磁盤中,當達到一定條件,將內存中的數據合並寫入到磁盤,即落地到磁盤。這樣做的目的是提高性能,但同時也埋下了隱患。在這個過程中,如果服務器宕機,內存中數據將會丟失,重啟服務器后,通過redo log日志recovery重做日志,保障了數據不會丟失。因此只要事務能夠實時寫入到磁盤(redo log),InnoDB存儲引擎就不會丟失數據。
如何控制事務寫入到磁盤(redo log)的時機哪? 通過配置參數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性能來說比較低。
如果設置為0最不安全數據會丟失,性能為最高的。
如果設置為2,DML性能要比設置為1高許多倍。
如果可以接受丟失innodb_flush_log_at_timeout(默認為1s)時間內的數據,建議設置innodb_flush_log_at_trx_commit=2。
二、主從復制層面丟失數據
我們先了解一下binlog的刷新機制以及MySQL的內部XA事務是如何保證binlog與redo log的一致性的。
1、內部XA事務原理
MySQL XA分為兩類,內部XA與外部XA;
內部XA用於同一實例下跨多個引擎的事務,由Binlog作為協調者;
外部XA用於跨多個MySQL實例的分布式事務,需要應用層介入作為協調者(崩潰時的懸掛事務,全局提交還是回滾,需要由應用層決定,對應用層的實現要求較高);
最常見的內部XA事務存在於binlog與InnoDB存儲引擎之間,從而保證了主從環境的數據一致性。在事務提交時,先寫binlog日志,然后再寫由InnoDB存儲引起的redo日志。對於這個操作過程,要求必須是原子性的,即兩者都要寫入成功。內部XA事務機制就是來保障binlog和redo log都寫入成功。
內部XA事務簡化的大致流程:
①、事務提交后,InnoDB存儲引擎會做一個prepare操作,將事務的XID寫入到redo log中。
②、寫binlog日志。
③、再該事務的commit信息寫入到redo log中。
如果是在步驟①和②時失敗,整個事務回滾。
如果是在步驟③時失敗,MySQL在重啟后會首先檢查UXID是否已經提交,若沒有提交,則在存儲引擎再執行一次提交操作。這樣就保障了redo log和binlog數據的一致性,防止數據丟失。
2、binlog刷新機制
我們從內部的XA事務知道,Master寫binlog。Binlog日志是如何寫、什么時機寫?分析控制參數sync_binlog是如何做的:
= 0 :表示MySQL不控制binlog的刷新,由文件系統自己控制它的緩存的刷新
> 0 :表示每sync_binlog次事務提交,MySQL調用文件系統的刷新操作將緩存刷下去
其中最安全的就是sync_binlog設置為1,表示每次事務提交,MySQL都會把binlog緩存刷下去,這樣在掉電等情況下,系統才有可能丟失1個事務的數據。同時對系統的IO消耗也是非常大的。
3、Master非實時寫redo和binlog丟失數據
我們從存儲引擎層面丟失數據章節中知道,如果innodb_flush_log_at_trx_commit沒有設置為1,仍會丟數據的。
如果嚴格要求保證數據不丟失,必須設置redo log和bin log實時刷盤。但是保證的數據的安全性,卻性能下降了。
4、slave非實時寫redo和binlog丟失數據
如果在Master日志記錄,事務提交均正常。而在slave出現異常甚至宕機,此時數據會丟失么?
我們知道主從同步機制中SQL Thread的作用是事件重放。在slave機器上會存在三個文件來保證事件的正確重放: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個文件如果不及時落地,則MySQL crash后會導致數據的不一致。
5、Master宕機后無法及時恢復造成的丟失數據
當master出現故障后,binlog未及時傳到slave,或者各個slave收到的binlog不一致。且master無法在第一時間恢復,這個時候我們該怎么處理?
如果master不切換,則整個數據庫只能只讀,影響應用的運行。
如果將某個的slave提升為新的master,那么原master未來得及傳到slave的binlog的數據則會丟失,並且還涉及到下面2個問題。
①、各個slave之間接收到的binlog不一致,如果強制拉起一個slave,則slave之間數據會不一致。
②、原master恢復正常后,由於新的master日志丟棄了部分原master的binlog日志,這些多出來的binlog日志怎么處理?
對於上面出現的問題,
一種方法是確保binlog傳到從庫,或者說保證主庫的binlog有多個拷貝。
第二種方法就是允許數據丟失,制定一定的策略,保證最小化丟失數據。
①、確保binlog全部傳到從庫
方案一:使用semi sync(半同步)方式,事務提交后,必須要傳到slave,事務才能算結束。對性能影響很大,依賴網絡適合小tps系統。
方案二:雙寫binlog,通過DBDR OS層的文件系統復制到備機,或者使用共享盤保存binlog日志。
方案三:在數據層做文章,比如保證數據庫寫成功后,再異步隊列的方式寫一份,部分業務可以借助設計和數據流解決。
②、保證數據最小化丟失
上面的方案設計及架構比較復雜,如果能容忍數據的丟失,可以考慮使用淘寶的TMHA復制管理工具。
當master宕機后,TMHA會選擇一個binlog接收最大的slave作為master。當原master宕機恢復后,通過binlog的逆向應用,把原master上多執行的事務回退掉。
參考
《高性能MySQL》