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