最近發(fā)生了一次 binlog 復(fù)制服務(wù)的一次故障, 記錄一下.
0x00 背景
線上有個(gè) MySQL 服務(wù)器, 通過 binlog 同步將數(shù)據(jù)更新發(fā)送到 Kafka, 供后續(xù)搜索等服務(wù)消費(fèi). MySQL 權(quán)限僅給了 db1
的所有 Table 的 SELECT
權(quán)限和 Slave 權(quán)限.
0x01 故障過程
由于要做線上 schema 變更, 因此同事在 test
庫上作了一次預(yù)演:
- 創(chuàng)建了一個(gè)臨時(shí)表
tmp_table1
- 完成線上 schema 修改的預(yù)演實(shí)驗(yàn)
- DROP 了上面的
tmp_table1
整個(gè)過程也就幾分鐘, 但對(duì)于 binlog 復(fù)制服務(wù)來說:
- 創(chuàng)建新的表, binlog 中收到了一個(gè) TableMapEvent
- 然而所屬的 MySQL user 并沒有該表的 DESC 權(quán)限, 獲取
tmp_table1
的詳細(xì)信息時(shí)報(bào)錯(cuò)沒有權(quán)限, 同步服務(wù)就此卡住 - 過了一段時(shí)間, 主從復(fù)制 lag 到達(dá)閾值, 觸發(fā) pagerduty 告警
- on-call 工程師接到告警, 查看日志定位到問題后, 試圖通過賦予binlog 復(fù)制服務(wù)的 MySQL 賬戶
*.*
的SELECT
權(quán)限解決問題, 但tmp_table1
已經(jīng)被DROP
掉, 因此 binlog 服務(wù)卡住了, 服務(wù)無法恢復(fù) - 隨后工程師決定跳過這些 binlog 的位置, 但 MySQL binlog 的 offset 是文件中的物理位置, 而不是類似 Kafka 的 offset 一樣的類似日志 ID, 猜測了好幾次才得以找到一個(gè)正確的 offset
整個(gè)過程下來, TTR(Time-To-Recover)估計(jì)有30分鐘.
0x02 事后總結(jié)
整個(gè)事故有什么可以學(xué)習(xí)到的呢?
1. binlog 復(fù)制服務(wù)不適合的場景
所有的架構(gòu)都是有適用場景的, binlog 復(fù)制也不例外, 可以學(xué)習(xí)到的是, 如果是那種經(jīng)常 DROP
表的 MySQL 實(shí)例(少見, 但這次不就是構(gòu)造了這種場景么), 在沒有做 binlog 特殊處理的情況下是不適合的, 非常容易觸發(fā)這種 binlog 復(fù)制服務(wù)找不到對(duì)應(yīng)的表的情況
2. 線上數(shù)據(jù)庫操作必須在嚴(yán)格的 staging 環(huán)境預(yù)演后再進(jìn)行
線上數(shù)據(jù)操作, 在嚴(yán)格的
staging 環(huán)境預(yù)演, 注意是 嚴(yán)格的
staging 環(huán)境. 上述問題, 如果是隨便 setup 的staging 環(huán)境, 有些細(xì)節(jié)跟線上不同, 就會(huì)失去提前暴露問題的機(jī)會(huì). 通過自動(dòng)化, 確保線上環(huán)境和 staging 環(huán)境除了數(shù)據(jù)和密碼不一樣, 其他都保持一致, 有時(shí)候是"救命"的
這個(gè)故障剛過, 當(dāng)天夜里 MySQL --> Hive 的全量同步服務(wù)也出現(xiàn)了故障: 做線上 schema 更改, 使用的工具創(chuàng)建了_
開頭的臨時(shí)表在線上庫, 但 _
開頭的表在 Hive 中是違法的表名, 導(dǎo)致數(shù)據(jù)同步完成后在 Hive 中創(chuàng)建表失敗. 一天兩個(gè)故障, 身心俱疲[捂臉].
3. 監(jiān)控一定要全面
出問題不可怕, 可怕的是"用戶"比你先發(fā)現(xiàn), 反過來通知你就尷尬了. 因此告警一定要覆蓋全面, 一旦出現(xiàn)問題, 立馬通知受影響的"用戶"(內(nèi)部系統(tǒng)或者外部用戶). 欣慰的是, 這兩次故障都是告警系統(tǒng)發(fā)現(xiàn)的.
0x03 Chaos Engineering
最后還是想, 還是要努力推進(jìn) chaos engineering, 平時(shí)通過錯(cuò)誤注入觸發(fā)錯(cuò)誤, 再回頭看自己系統(tǒng)的健壯性才是正解...
-- EOF --