小紅書自研Binlog Server守護MySQL數(shù)據(jù)0丟失

異常情況下的數(shù)據(jù)丟失問題將極大地影響業(yè)務的可用性,尤其在一些核心場景的數(shù)據(jù)恢復過程更是耗時耗力。在業(yè)務支持上我們亟需一種方案,當數(shù)據(jù)庫在發(fā)生故障既要保證數(shù)據(jù)一致性也要減少切換時間,盡可能減少甚至徹底免除人工的介入。
小紅書數(shù)據(jù)庫團隊提出一種基于Binlog Server的數(shù)據(jù)一致性解決方案,通過提升半同步復制性能,加速日志傳輸,在故障時可無侵入現(xiàn)有數(shù)據(jù)庫架構地實現(xiàn)自動補數(shù)據(jù),保證數(shù)據(jù)一致性?,F(xiàn)推出的自研 Binlog Server 與 ORC 高可用方案,經(jīng)過實踐已證明可做到:
1)使用極少的資源(1C1G)即可將復制速度提升至300MB/s+,實現(xiàn)了復制性能翻倍;
2)優(yōu)化了故障切換效率,按照一致性優(yōu)先原則使用Binlog Server為新主庫補數(shù)據(jù),有效降低運維成本和業(yè)務風險,實現(xiàn)數(shù)據(jù)庫快速數(shù)據(jù)恢復。
目前該方案已經(jīng)100%部署于小紅書半同步復制集群,在多次切換中為新主庫補數(shù)據(jù),守護了核心數(shù)據(jù)庫的安全。
01、簡介
2017年 GitLab 數(shù)據(jù)庫工程師誤操作,導致18個小時的服務中斷并且部分數(shù)據(jù)永久性丟失,丟失數(shù)據(jù)影響了大約 5000 個客戶和 700 個項目。
2022年某云廠商部分區(qū)域 RDS 服務不可用,影響了業(yè)務大約 3+小時。
2023年某云數(shù)據(jù)庫出現(xiàn)了自動清理數(shù)據(jù)的 Bug,導致部分用戶的最新數(shù)據(jù)刪除且不可恢復。
2023年 Digital Ocean 托管的部分數(shù)據(jù)庫異常切換,導致部分用戶永久丟失 2-5 分鐘數(shù)據(jù)。
核心場景的數(shù)據(jù)庫一旦出現(xiàn)了數(shù)據(jù)丟失,會極大的影響了業(yè)務的可用性。因此,數(shù)據(jù)庫的高可用性和一致性始終是核心業(yè)務系統(tǒng)的關鍵訴求。比如,DBA 同學也剛剛經(jīng)歷了這樣驚心動魄的一幕:
?? 某天,數(shù)據(jù)庫 P0 告警突發(fā),核心集群主庫宕機!XX同學需要立即與上下游業(yè)務緊急聯(lián)動,定位到數(shù)據(jù)丟失并完成數(shù)據(jù)修正。經(jīng)過一個小時的“救火”操作后,業(yè)務才逐漸恢復正常。這一“救火”場景出現(xiàn),既消耗了大量的人力成本,也影響了系統(tǒng)整體穩(wěn)定性。因此,亟需一種方案,當數(shù)據(jù)庫在發(fā)生故障切換下也能自動保障數(shù)據(jù)一致性和完整性,徹底免除人工介入。
在展開方案介紹之前,我們先來了解 2 個知識:
?? 什么是 RPO=0?RPO=0 意味著在任何切換或災難發(fā)生后,數(shù)據(jù)一條都不丟! 如果實現(xiàn)了 RPO=0,當數(shù)據(jù)庫發(fā)生宕機時,新的數(shù)據(jù)庫能夠100% 自動補齊所有數(shù)據(jù),上下游業(yè)務不用再擔心數(shù)據(jù)修復事務,極大降低運維壓力和業(yè)務風險,在高可用里面實現(xiàn)了數(shù)據(jù)恢復的閉環(huán)。
?? 業(yè)內主流OLTP數(shù)據(jù)庫如何實現(xiàn) RPO=0?業(yè)內主流方案主要分為以下三種:



MySQL 復制分為異步復制和半同步復制,其中半同步復制要求從庫至少有一臺復制成功響應,這樣保證至少一臺從庫保存了日志數(shù)據(jù)。所以半同步復制的速度決定了主庫寫入速度的上限。但社區(qū)半同步實現(xiàn)復雜,其復制速度較慢,影響了主庫寫入速度。Facebook方案使用 Binlog Server 加快半同步復制速度,從而提升了主庫寫入性能,讓更多的場景可以使用半同步復制。如果復制延遲太大(網(wǎng)絡或者CPU耗盡等場景下),復制會出現(xiàn)自動降級,從半同步復制退化為異步復制,被稱為半同步退化,半同步退化將影響數(shù)據(jù)一致性(本質是一種異步復制場景)。
基于方案復雜性和穩(wěn)定性考慮,我們對比后決定采用 Binlog Server 方案,來實現(xiàn)小紅書 MySQL 的 RPO=0,并滿足以下條件:
- 將半同步復制速度翻倍,Binlog復制速度提升到 300MB/s+;
- 對現(xiàn)有 MySQL 架構和復制方案無侵入;
- 無縫支持現(xiàn)有 MySQL 高可用架構的切換方案。
1.1 收益概述
相對于社區(qū)MySQL半同步+開源ORC高可用組件,自研Binlog Server+自研ORC高可用在半同步復制速度、輕量化部署、數(shù)據(jù)一致性優(yōu)先和運維便捷性等場景進行優(yōu)化,保證 RPO=0。

得益于Binlog Server性能的提升,當出現(xiàn)寫入大壓力的場景時,Binlog Server可以跟上主庫寫入速度,并且在故障場景下為新主庫提供日志數(shù)據(jù)。


1.2 性能驗證
當前線上采用了同城異可用區(qū) Binlog Server 部署形式,也就是在 MySQL 集群內部署一個同城、不同機房、半同步連接的 Binlog Server 實例,如下圖所示:

這樣做的目的是為了提升 MySQL 故障恢復的“數(shù)據(jù) 0 丟失”半徑 ,確保在發(fā)生機房級故障時,ORC 的切換機制依然能夠保證數(shù)據(jù)的一致性。Binlog Server 的性能顯著優(yōu)于普通從庫,在小事務壓測條件下,其寫入速度可達 300MB/s,且資源消耗極低。憑借高吞吐、低資源消耗的優(yōu)勢,Binlog Server 完全可以部署于異地機房,大幅提升系統(tǒng)在機房級故障下的數(shù)據(jù)恢復能力,實現(xiàn) 0 數(shù)據(jù)丟失的主庫故障切換。

1.3 切換效果驗證
得益于 Binlog Server 的高吞吐能力,以及半同步復制特性,可以確保 Binlog Server 中的數(shù)據(jù)始終保持最新。即使遇到如機房斷網(wǎng)等嚴重故障場景,結合 ORC 的選主策略,可以實現(xiàn) MySQL 故障切換過程中數(shù)據(jù) 0 丟失。

目前 Binlog Server 配合 ORC 的數(shù)據(jù)一致性方案已經(jīng)開始灰度,線上已經(jīng)覆蓋到半同步核心集群,半同步集群覆蓋比例達到100%。
下面的案例的切換效果如下,詳細展示了切換過程中Binlog Server作為臨時主庫給下游補充數(shù)據(jù)。

02、需求分析
2.1 復制速度分析
首先關注一下影響主從延遲的原因。下圖是MySQL主從復制的完整的數(shù)據(jù)鏈路圖。綠色框表示執(zhí)行線程,黃色框表示實例(主庫/從庫)的Binlog,因為Binlog Event中帶有時間戳,所以主從延遲表示為從庫的Binlog時間 - 主庫的Binlog時間。紅色的框為Relaylog,可以理解為從庫來不及處理的數(shù)據(jù)的臨時在磁盤存儲的文件。紫色為每個worker thread對應的處理隊列(內存結構)。
從圖中可以看到,整個復制鏈路經(jīng)歷的環(huán)節(jié)特別多,任何一個環(huán)節(jié)速度跟不上都會造成主從延遲比較大。其中IO Thread和Dispatch Thread之間的干擾會降低半同步復制速度(包括不限于共享Mutex鎖,IO串行化,共享復制位點信息等),如果IO線程處理速度慢將影響Master節(jié)點對業(yè)務響應的速度。復制速度慢一直是MySQL社區(qū)存在的問題,否則也不會有各種Binlog Server方案。

2.2 行業(yè)方案調研
在MySQL不滿足需求的情況下,參考調研市場上已有的Binlog Server,總結各個方案的優(yōu)缺點。

根據(jù)行業(yè)的調研和數(shù)據(jù)庫現(xiàn)狀,從上面的部署來看,F(xiàn)acebook Binlog Server方案是最合適的,其功能豐富,和現(xiàn)有系統(tǒng)能兼容。但其實現(xiàn)未開源,將按照其設計思路設計自研Binlog Server。
2.3 需求定位
- 功能:支持半同步,提供RPO=0的方案;支持級聯(lián)架構,在主從切換時為從庫補充Binlog數(shù)據(jù);
- 性能:半同步復制時提供更高復制速度,相對于主庫無丟失;
- 運維:支持MySQL管理命令,無需外部系統(tǒng)改造;輕量化部署,1C1G資源即可滿足需求;
- 穩(wěn)定性:支持crash recovery,保證數(shù)據(jù)一致性;
- 兼容性:兼容MySQL 生態(tài)的各種解析工具,無需單獨開發(fā)。
03、方案設計
Binlog Server架構與其基本數(shù)據(jù)流和控制流基本如下圖所示。下圖所示為級聯(lián)架構(對Binlog Server來說最復雜的場景),同時支持Master和Slave分別作為上下游,表現(xiàn)類似一個MySQL實例。其中綠色線表示Binlog Server控制流,主要是從Admin管理員發(fā)送的管控SQL,在SQL解析器處理后,在Manage模塊進行處理,可以對MasterSession(和主庫的連接),SlaveSession(和下游從庫的連接),Binlog(本地存儲文件)進行管理。紅色線就是數(shù)據(jù)流,主要是存放主庫發(fā)送來的Binlog數(shù)據(jù),存儲在本地并通過SlaveSession向下游發(fā)送。具體模塊將一一展開介紹。

下面介紹3.1-3.5節(jié)為各個模塊實現(xiàn)細節(jié),3.6節(jié)將介紹Binlog Server和ORC配合主從切換。
3.1 MySQL協(xié)議支持
為了滿足上面的數(shù)據(jù)流程,需要支持如下協(xié)議的解析&處理:
- BinlogServer->Master,Slave->BinlogServer進行認證、連接和狀態(tài)獲取SQL
- Admin->BinlogServer管理線程的認證和連接
- Admin->BinlogServer發(fā)送的COM命令
- BinlogServer->Admin發(fā)送的ResultSet
- Master->BinlogServer, BinlogServer->Slave發(fā)送的Event格式
這里需要按照MySQL協(xié)議規(guī)定的注冊、握手、COM格式、ResultSet格式以及EVENT格式處理, 對于協(xié)議的發(fā)送將復用MySQL Client的庫,但是協(xié)議解析和數(shù)據(jù)包封裝需要Binlog Server處理。以Binlog為例,其基本格式如下圖所示,需要分別對Header和Footer處理,提取各個字段。

3.2 SQL語法支持
為了方便ORC進行管理,對現(xiàn)有的運維系統(tǒng)和高可用系統(tǒng)無侵入。需要Binlog Server支持SQL語法,可以減少周圍系統(tǒng)的開發(fā)和適配成本。在這里我們制作了一個語法解析器支持特定SQL類型。聯(lián)調和部署時遇到新SQL支持,十分方便進行新語法的支持。
這里無法采用MySQL解析器,因為MySQL的詞法解析部分全部自行編寫,而不是采用FLEX(全局變量,不支持多線程并發(fā)),以提升SQL解析性能,從而支持每秒幾十萬次的SQL解析速度,缺點就是代碼非常復雜,難以將所需功能進行剝離。但是Binlog Server的SQL語法只是用來做運維管控,沒有對高并發(fā)的需求,所以在FLEX詞法解析過程即使串行化,依然有幾千次的解析速度,完全滿足管控SQL需求。可以使用AI寫bison和flex文件語法解析器文件,效果非常好。
如下圖所示,當輸入的字符串經(jīng)過詞法解析器(取出token)和語法解析器(獲得語法樹),就可以提取需要的語法樹信息。

從整個SQL執(zhí)行視角來看,SQL執(zhí)行分為三個階段:
- 語法解析階段
- SQL命令執(zhí)行階段
- SQL結果輸出階段
支持 start slave 和 stop slave 等運維命令,保持slave的啟停方式和MySQL一致。以start salve為例,下面展示了 SQL 解析和執(zhí)行過程,首先會建立一個新 session 和 MySQL 客戶端保持聯(lián)系,然后用戶發(fā)出 start slave 命令,按照COM_QUERY進行解析。在自定義語法解析器中,將start slave 標記為 SQLCOM_START_SLAVE,然后執(zhí)行 節(jié)點注冊過程(詳細過程見下一節(jié)),最終返回結果集。

3.3 節(jié)點注冊
Binlog Server支持級聯(lián)架構,既可以作為Slave節(jié)點從上游接收并保存Binlog,也可以作為Master向下游發(fā)送Binlog。因此其必須具有雙向注冊能力,需要遵守MySQL節(jié)點注冊的規(guī)范,模擬作為從庫或者主庫進行注冊。


3.4 半同步支持
和異步復制相比,半同步發(fā)送Binlog過程有變化,這將影響B(tài)inlogServer處理的過程。如下圖所示,Master節(jié)點生成了Binlog文件(由一個個Event構成),在發(fā)送Event時,會在每個Event前添加Header。每個Header由2字節(jié)構成,第一字節(jié)為0xEF(Magic Number)用于校驗,第二字節(jié)為0x1/0x0,用于指示從庫是否發(fā)送ACK。當數(shù)據(jù)發(fā)送到BinlogServer以后,會先處理Header信息,確定是否需要發(fā)送ACK,如果需要發(fā)送,就將MasterLog位點信息發(fā)送給主庫。主庫收到ACK后確認這些數(shù)據(jù)已經(jīng)在下游持久化,即可在主庫InnoDB進行提交(AFTER_SYNC模式)。
這里需要注意一點,如果ACK信息丟了,后面的ACK確認的位點會自動包含前面的位點,這樣保證發(fā)送過程不至于中斷。

3.5 文件管理和數(shù)據(jù)一致性
Binlog文件管理主要分為兩部分,第一是Binlog文件管理;第二是Crash時的數(shù)據(jù)一致性。
- 文件管理:參考MySQL Binlog設計,使用索引文件記錄Binlog元數(shù)據(jù)信息。所以采用索引文件+數(shù)據(jù)文件結合的方式記錄。
- 數(shù)據(jù)一致性:Crash Safe重點強調宕機場景(非預期場景),但是對于預期內的關機也保持了一致性。

為了保證Binlog索引文件和數(shù)據(jù)文件的一致性,參考MySQL修改Binlog的方式。變動時先修改Binlog索引文件,創(chuàng)建新的臨時文件,然后再修改Binlog數(shù)據(jù)文件,最后將臨時的索引文件覆蓋寫為正式的索引文件。這個過程中如果出現(xiàn)了宕機等場景,那么根據(jù)臨時文件和Binlog文件是否修改,決定該操作是提交還是回滾,從而保證了數(shù)據(jù)的一致性。
3.6 高可用支持
當主庫宕機時,希望最終選舉出來一個同機房的新主庫為業(yè)務提供服務。但是同機房的從庫不一定是數(shù)據(jù)最多的從庫。因此需要增加補數(shù)據(jù)的環(huán)節(jié),如下圖所示。

高可用組件ORC在數(shù)據(jù)切換的時候,會分為兩個階段進行。
1M:根據(jù)GTID最大原則選出第一輪備選主庫,開始補數(shù)據(jù)。這一輪沒有偏好,盡量選擇GTID最長的作為Master來給所有節(jié)點補數(shù)據(jù)。
2M:根據(jù)同機房 & GTID最長選擇第二輪主庫,作為新的Master。這一輪存在偏好,例如選擇和老主庫在同一個機房的Slave作為新主庫。

主庫故障和恢復流程簡化過程上圖所示。部署形態(tài)上采用Master掛載2個半同步的BinlogServer以保證數(shù)據(jù)不丟失,Master同時掛載若干個異步復制的Slave節(jié)點。
如果主庫宕機(不可恢復),如圖2所示:
- ORC 1M階段,將選擇兩個Binlog Server中GTID最長的一個作為臨時Master,給其他所有節(jié)點補數(shù)據(jù)。這一輪選舉大概率是同機房的Binlog Server。如果同機房Slave和Binlog Server GTID相同(寫入量很小,所有機器數(shù)據(jù)沒有延遲), ORC選擇沒有偏好,能將GTID補上即可。
- ORC 2M階段,同機房的Slave將作為新的主庫,將其他所有的節(jié)點掛載到新主庫上。
- 經(jīng)過ORC 2M階段,數(shù)據(jù)庫集群即可對外提供服務。同時在本機房內,異步補充一臺新從庫。
- 新從庫補充完畢后,整體的部署將和故障前完全一致。
在這個過程中,Binlog Server的添加不會對ORC選舉額外造成負擔。因為1M選擇時,仍然按照GTID最長原則。所以Binlog Server提供足夠高的性能后,ORC會自然選擇到Binlog Server。只是在2M選擇的時候,需要排除Binlog Server作為新的備選主庫即可。
04、未來展望
Binlog Server是一個消耗資源極少的輕量化Binlog 存儲節(jié)點(1C1G即可)。除了提供一致性解決方案以外,未來也可以在其他使用Binlog的場景中發(fā)揮作用。
- 在從庫擴容、庫表拆分的場景中使用Binlog Server補充Binlog數(shù)據(jù);
- Binlog Server支持標準MySQL協(xié)議,DTS、Canel等可以從Binlog Server拉取數(shù)據(jù),降低主庫壓力;
- Binlog Server后端使用S3(配合S3FS)作為存儲,將節(jié)省Binlog保存成本,保持更長時間的Binlog數(shù)據(jù)。
05、作者簡介
張凡凡
小紅書關系型數(shù)據(jù)庫研發(fā)工程師,主要負責小紅書關系型數(shù)據(jù)庫內核研發(fā)。
周旭峰
小紅書關系型數(shù)據(jù)庫研發(fā)工程師,主要負責小紅書關系型數(shù)據(jù)庫高可用系統(tǒng)研發(fā)。

































