MySQL数据库层丢数据场景_MySQL數據庫丟失數據場景分析

引擎層數據丟失場景

1.1.InnoDB丟失數據場景分析

InnoDB作為MySQL支持事務的存儲引擎,同Oracle類似,事務提交時需要寫redo、undo log。 InnoDB采用預寫日志(Write Ahead Log)策略,數據頁的變更會首先在內存中完成,同時將事務操作順序記錄到redo log中。完成上述的操作后表示該事務已經完成,可以返回給應用已提交的信息。 但此時實際被更改的數據頁還保存在內存中(稱為臟頁),並沒有flush到磁盤上即沒有落地。刷盤操作一般會在達到一定條件后,觸發checkpoint機制,此時會將內存中的臟頁合並寫入到磁盤里,完成數據同步。

刷盤策略

由innodb_flush_log_at_trx_commit參數控制的策略。如下圖:

d3eeed5ebe7bd6e3bdaa78327a943c76.png

參數innodb_flush_log_at_trx_commit:

=0 :每秒 write os cache & flush disk

=1 :每次commit都 write os cache & flush disk

=2 :每次commit都 write os cache,然后根據innodb_flush_log_at_timeout參數(默認為1s) flush disk

innodb_flush_log_at_trx_commit=1最為安全,因為每次commit都保證redo log寫入了disk。但是這種方式性能對DML性能來說比較低,在我們的測試中發現,如果設置為2,DML性能要比設置為1高10倍左右(所以在短信平台出現寫入問題時,已將該參數設置為2)。

innodb_flush_log_at_trx_commit為0或2的區別主要體現在在mysql service crash或system crash時丟失事務的類型:

1)當mysql service crash時,設置為0就會丟失1秒內的所有已提交及未提交的事務,且無法回滾(因為redo log還記錄在log buffer中,沒有落盤到redo log)。而設置為2時,每次提交都會寫入到os cache中,即使service crash掉,也只會丟失1秒內所有未提交的事務,而已提交的事務已經寫入redo log中,可以回滾。

2)當system crash時,與上述類似。

因此,業內的共識是在一些DML操作頻繁的場景下,參數innodb_flush_log_at_trx_commit設置為2。

雖然這樣就存在丟數據的風險:當出現mysql service crash時,重啟后InnoDB會進行crash recovery,則會丟失innodb_flush_log_at_timeout秒內的已提交的數據。未提交的數據則可由應用中的事務補償機制處理。但是IO性能可以提高至少10倍。

PS:當開啟了內部XA事務(默認開啟),且開啟binlog,情況稍有不一樣。見下文。

1.2.MyISAM丟失數據場景分析

MyISAM存儲引擎在我們的生產環境中基本沒有使用。而且我們線上的5.6版本已將系統的數據字典表元數據表等系統表的默認存儲引擎修改為InnoDB。

由於MyISAM不支持事務,且沒有data cache,所有DML操作只寫到OS cache中,flush disk操作均由OS來完成,因此如果服務器宕機,這部分數據肯定會丟失。

2.主從復制導致數據不一致的場景

MySQL主從復制原理:MySQL主庫在事務提交時寫binlog,並通過sync_binlog參數來控制binlog刷新到磁盤“落地”。從庫中有兩個線程: IO線程負責從主庫讀取binlog,並記錄到本地的relay log中;SQL線程再將relay log中的記錄應用到從庫。如下圖所示:

fae00e376011f239306c80e228a37171.png

2.1.binlog刷新機制

master寫binlog與innodb引擎寫redo類似,由參數sync_binlog控制:

= 0 :表示MySQL不控制binlog的刷新,由文件系統控制binlog cache的刷盤操作

= N :表示每sync_binlog在N次事務提交后,MySQL調用文件系統的flush操作將binlog cache中的內容刷盤

sync_binlog=1時最安全,即表示每次事務提交,MySQL都會把binlog cache中的內容flush disk。這樣在掉電等情況下,系統只有可能丟失1個事務的數據。但是sync_binlog為1時,系統的IO消耗非常大。

但是N的值也不易過大,否則在系統掉電時會丟失較多的事務。當前我們生產系統設置為100.

2.2.內部XA事務原理

MySQL的存儲引擎與MySQL服務層之間,或者存儲引擎與存儲引擎之間的分布式事務,稱之為MySQL內部XA事務。最為常見的內部XA事務存在與binlog與InnoDB存儲引擎之間。在事務提交時,先寫二進制日志,再寫InnoDB存儲引擎的redo log。對於這個操作要求必須是原子的,即需要保證兩者同時寫入。內部XA事務機制就是保證兩者的同時寫入。

XA事務的大致流程:

1)事務提交后,InnoDB存儲引擎會先做一個PREPARE操作,將事務的XID寫入到redo log中

2)寫binlog

3)將該事務的commit信息寫到redo log中

9fbaf70e5676f38cc9c371b6e5bfb63c.png

如果在步驟1和步驟2失敗的情況下,整個事務會回滾,如果在步驟3失敗的情況下,MySQL數據庫在重啟后會先檢查PREPARE的XID事務是否已經提交,若沒有,則在存儲引擎層再進行一次提交操作。這樣就保證了redo與binlog的一致性,防止丟失事務。

2.3.主庫寫redo log、binlog不實時造成的數據不一致

上面我們介紹了MySQL的內部XA事務流程,但是這個流程並不是天衣無縫的,redo的ib_logfile與binlog日志如果被設置非實時flush,就有可能出現以下數據不一致的情況:

1)Redo log的trx_prepare未寫入,但binlog已寫入,則crash recovery后從庫數據比主庫多。

2)Redo log的trx_prepare與commit都寫入了,但binlog未寫入,則crash recovery后從庫數據量比主庫少。

從目前來看,只能犧牲性能去換取數據的安全性,必須要設置redo log和binlog為實時刷盤,如果對性能要求很高,則考慮使用SSD來替代機械盤。

2.4.從庫寫redo log、binlog不實時造成的數據不一致

主庫正常,但是從庫出現異常情況宕機,如果數據丟失,從庫的SQL線程還會重新應用嗎?這個我們需要先了解SQL線程的機制。

從庫讀取主庫的binlog日志后,需要落地3個文件:

relay log:即IO Thread讀取過來的主庫binlog,內容格式與主庫的binlog一致

relay log info:記錄SQL Thread應用的relay log的位置、文件號等信息

master info:記錄IO Thread讀取主庫的binlog的位置、文件號、延遲等信息

因此如果當這3個文件如果不及時落地,則system crash后會導致數據的不一致。

在MySQL 5.6.2之前,從庫記錄的主庫信息以及從庫應用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個參數控制刷新:

1)sync_relay_log:默認為10000,即每10000次sync_relay_log事件會刷新到磁盤。為0則表示不刷新,交由OS的cache控制。

2)sync_master_info:若master-info-repository為FILE,當設置為0時,則每次sync_master_info事件都會刷新到磁盤,默認為10000次刷新到磁盤;若master-info-repository為TABLE,當設置為0時,則表不做任何更新,設置為1,則每次事件會更新表。默認為10000。

3)sync_relay_log_info:若relay_log_info_repository為FILE,當設置為0時,交由OS刷新磁盤,默認為10000次刷新到磁盤;若relay_log_info_repository為TABLE,則無論為任何值,每次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性能瓶頸。

2.5.主庫宕機后無法及時恢復造成的數據不一致

當主庫出現故障后,binlog未及時拉到從庫中,或者各個從庫收到的binlog不一致(多數是由於網絡原因)。且主庫無法在第一時間恢復:

1)如果主庫不切換,則應用只能讀寫主庫。如果有讀寫分離的場景則會影響應用(讀寫分離場景中從庫會從)。

2)如果將某一從庫提升為新的主庫(如MHA),那么原主庫未來得及傳到從庫的binlog數據則會丟失,並且還涉及到下面2個問題:

a)各個從庫之間接收到的binlog不一致,如果強制拉起一個從庫做新主庫,則從庫之間數據會不一致。

b)原主庫恢復正常后,由於新的主庫日志丟棄了部分原主庫的binlog日志,那么會多出來故障時期的這部分binlog。

對於上面出現的問題,業內已經有較成熟的方法來解決:

2.5.1確保binlog全部傳到從庫

方案一:使用semisync replication(半同步復制)插件。半同步復制的特點是從庫中有一台提交后,主庫才能提交事務。優點是保證了主、從庫的數據一致性;缺點是對性能影響很大,依賴網絡,適合tps壓力小的場景。

方案二:雙寫binlog,通過DBDR OS層的文件系統復制到備機,或者使用共享盤保存binlog日志。優點和方案一類似,但此方案缺點較明顯:

1)DBDR需要部署自己的服務

2)DBDR腦裂嚴重。在發生災難場景時,往往不能正確切換。

3)需要建立heartbeat機制。保證被監控機的存活。

方案三:架構層面調整,引入消息隊列做異步消息處理。比如保證數據庫寫成功后,再異步隊列的方式寫一份,部分業務可以借助設計和數據流解決。

2.5.2保證數據最小化丟失

上面的方案設計及架構比較復雜,如果能容忍數據的丟失,可以考慮使用MHA。

當master宕機后,MHA可以指定一台或者選延遲最低或者binlog pos最新的一台從庫,並將其提升為主庫。

MHA在切換master后,原master可以修復后以新master的slave角色重新加入集群。從而達到高可用。

3.總結

通過上面的總結分析,MySQL丟數據的場景眾多,主要還是涉及到引擎層數據丟失場景、主從的數據不一致場景等。

根據分布式領域的CAP理論(Consistency一致性、Availability高可用性、Partition tolerance分區耐受性),在任何的分布式系統只能同時滿足2點,沒辦法三者兼顧。MySQL的主從環境滿足Availability,且在半同步場景中數據可以做到數據強一致性,可以滿足Consistency。但是不能完全滿足Partition tolerance。

因此現在業內對於事務(數據)丟失的處理有很多解決方案,如事務補償機制、半同步復制、雙寫機制、異步消息隊列等等。甚至,還可以針對業務對CAP中哪兩者更有需求來選擇相應的數據產品,如需要分區耐受性和高可用兼顧時,可以使用Cassandra等列式存儲。都可以達到業務相應的數據一致性需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值