如何避免這10類常見故障?B站資料庫架構設計做了這5步……

語言: CN / TW / HK
# 一分鐘精華速覽 #
今年3月GitHub在一週內出現了多次服務不可用的情況,每起事件持續時長在 2-5 小時,據有媒體統計,GitHub在一週中多次中斷影響的開發者數量高達 7300 萬。事後GitHub 高階工程副總裁 Keith Ballinger 發文表示,「我知道這會影響許多客戶的生產力,我們也非常重視這一點。過去幾周發生的宕機事件根本原因是我們的‘MySQL1’叢集中的資源爭奪,在負載高峰期,影響了 GitHub 大量服務和功能。」 資料庫故障對企業系統服務的影響可見一斑,今天我將結合B站自身的實戰經驗來跟大家說說資料庫故障治理相關的心得:

作者介紹

B站DBA Leader-王志廣

十年以上資料庫運維經驗,曾在多家大型網際網路公司任職,主導和參與了多家資料庫私有云建設、資料庫多活、資料庫架構從商業資料庫到開源資料庫的遷移。目前主要專注於B站資料庫多活、資料庫服務治理等。


溫馨提醒:本文約4900字,預計花費8分鐘閱讀。

回覆 “交流” 進入讀者交流群;


一、什麼是資料庫故障

資料庫故障顧名思義就是資料庫相關的故障,它在學術上沒有明確的定義,故各公司一般以對應用的影響範圍來量化定義資料庫故障。如下圖所示:


二、常見的資料庫故障有哪些?

故障治理就得對症下藥,所以治理的第一步就是明確常見的資料庫故障有哪些,今天就MySQL、快取兩個大方向來跟大家一起梳理一下。  

1、MySQL

作為在網際網路公司廣泛使用的傳統意義上的資料庫,MySQL的故障可以分為以下幾種: 1.1 例項不可用 資料庫作為一種特殊的應用,在其生命週期內無法保證100%可用。資料庫例項不可用的原因一般有:
  • 硬體故障:硬體故障是生產環境中無法避免問題。常見的硬體故障包含了CPU、記憶體、磁碟、主機板等。
  • 系統故障:系統故障包含作業系統bug和資料庫bug。
  • 網路故障:網路故障包含網路裝置故障和專線故障。
  • 其他:如資源分配不合理導致的OOM。
1.2 資料延遲


資料延遲包含資料庫的主從延遲和依賴binlog的資料訂閱服務的延遲。 1.2.1 主從複製延遲 主從複製延遲是線上經常出現的問題之一,對於讀寫分離的業務來說,從庫延遲的影響可能是致命的。導致主從延遲的原因一般有:
  • 主庫執行了一個非常大的資料變更事務;
  • 主庫變更頻率過快, 導致從庫跟不上;
  • 使用原生的表結構變更語句進行大表的DDL ;
  • 從庫配置引數較低、硬體效能較差;
  • 從庫負載過高,導致效能變差 ;
  • 從庫MDL 鎖 ;
  • 主從之間網路問題 ;
  • 主從複製Bug (多執行緒複製 bug 最為常見)。
1.2.2 資料訂閱服務的延遲 binlog的消費是資料訂閱服務中的核心功能之一,對很多依賴binlog消費的業務來說,訂閱服務的延遲將導致線上業務的不可用,導致訂閱服務延遲的原因一般有:
  • 上游從庫延遲
  • Kafka效能瓶頸
  • canal類元件效能瓶頸
1.3 資料損壞 資料損壞隨時可能發生在任何人身上,沒有任何辦法可保證它不會發生。導致資料損壞的原因如下:
  • 未開啟雙一引數,在宕機場景下丟失;
  • 未使用半同步複製,在宕機場景下丟失;
  • 使用三方工具進行DDL的某些特殊場景下可能會丟, 比如pt-osc、gh-ost;
  • 誤操作、誤刪除資料或者蓄意刪除,也就是常說的運維人員刪庫跑路。
1.4 效能下降 資料庫的效能下降一般有以下幾種表現慢查詢增多、效能抖動、壓力過載。 1.4.1 慢查詢增多 慢查詢也是我們經常會遇到的場景,嚴重情況下會導致資料庫雪崩, 一般導致慢查詢增多有如下原因:
  • 新業務上線 SQL 效率較差或者沒有合適的索引;
  • 業務場景發生改變, 邊緣場景被觸發;
  • 資料傾斜, 導致未走合適的索引;
  • Innodb buffer pool 命中率低, 觸發大量物理讀;
  • 優化器 bug, 導致未走最優索引宿主機磁碟異常。
1.4.2 效能抖動 正常情況下資料庫響應時間比較穩定,但是資料庫一些內部行為也會導致資料庫偶爾出現效能抖動,比如對於 MySQL 來說刷髒(觸發同步刷髒時尤為明顯)。一般我們評估一個例項是否正常,會根據 P999 分位的響應時間來判斷, 當然還有其他的一些可能會導致效能抖動的原因, 比如慢查詢、批量操作、定時任務等。 1.4.3 壓力過載 壓力過載一般會出現在如下的一些場景:
  • 突發流量;
  • 上游快取失效;
  • 大型活動中流量超過預期。
 

2、快取

常見的快取故障包含以下幾種:快取穿透、快取擊穿、快取雪崩,下面展開詳細說說。 2.1 快取穿透 快取穿透是指查詢一個根本不存在的資料,快取層和持久層都不會命中。在日常工作中出於容錯的考慮,如果從持久層查不到資料則不寫入快取層,快取穿透將導致不存在的資料每次請求都要到持久層去查詢,失去了快取保護後端持久的意義。 造成快取穿透的原因有兩個。
  • 自身業務程式碼或者資料出現問題(例如:set 和 get 的key不一致)。
  • 一些惡意攻擊、爬蟲等造成大量空命中。
2.2 快取擊穿 快取擊穿主要是是以下兩種場景:
  • 當前key是一個熱點key(例如一個秒殺活動),併發量非常大。
  • 重建快取不能在短時間完成,可能是一個複雜計算,例如複雜的SQL、多次IO、多個依賴等。在快取失效的瞬間,有大量執行緒來重建快取,造成後端負載加大,甚至可能會讓應用崩潰。
2.3 快取雪崩 快取雪崩,一般是由大量的key集中在一段時間內失效,或者redis服務故障所引起的,導致查詢請求查不到快取,大量的請求湧入資料庫。 快取雪崩會導致以下兩種後果:
  • 資料庫瞬間被大量的流量打死
  • 該服務介面訪問時間過長,導致耗盡了執行緒資源,從而引起整個系統的崩潰。

三、B站在資料庫故障治理方面做了啥?


上面講述了常見的資料庫故障,下面我們來說說如何從資料庫架構設計上來規避故障,這裡會結合B站的一些實踐來跟大家分享。  

1、高可用

高可用(High Availability)是系統架構設計中必須考慮的因素之一,它通常是指,通過設計減少系統不能提供服務的時間。所以在做資料架構設計時一定要考慮HA 能力,無論是選用叢集模式(MGR、PXC 等),或是使用傳統主從複製加上其他高可用元件(Orchestrator、MySQL Replication Manager、MHA等),都可以幫我們在例項不可用時把損失最小化。

 

2、擴容


擴容一般分為垂直擴容和水平擴容兩種,對於傳統MySQL來講,垂直擴容的收益比通常不高, 而基於其本身狀態的水平擴容又很難做到快速,所以如何做應急擴容一直是業內比較頭疼的問題。 這時候引入雲原生資料庫是一個不錯的選擇,業內的雲原生資料庫大都進行了儲存計算分離改造,對於計算層可以通過 k8s 的HPA能力進行快速擴容,而儲存層則依賴於底層共享儲存或者分散式儲存也有不同的擴容方式。 在 B 站我們大量引入了 TiDB, 在一些適合彈性伸縮的場景下有不錯的收益。
  • 垂直擴容:增大 buffer pool、 cpu 限制等配額,需要平臺側完善 Quota 管理和變更能力。
  • 水平擴容:對於MySQL來講, 應急快速水平擴容只能用於讀負載,我們會有一些異地機房的例項,一般情況下用作災備以及離線用途, 在極端場景下可以通過平臺操作一鍵接入讀負載池;對於 TiDB 來講,我們把計算節點放入 k8s,通過k8s 的HPA能力進行水平彈性伸縮。

3、多活建設


多活是高可用的升級版,一般分為異地災備、同城多活、異地多活、兩地三中心、三地五中心等不同的架構設計。(回顧傳送門: B站SRE負責人親述713故障後的多活容災建設  
  • 異地災備: 當主機房的例項全部不可用時進行切換,一般是由高可用元件觸發切換,高可用元件本身應該是一個分散式跨機房的架構。 比如在B 站, 我們的高可用元件是三機房投票決策的,同時應當注意避免跨機房專線異常造成的誤判以及誤切換熔斷策略。
  • 同城雙活: 主庫在同城某一個 zone,從庫則分佈在同城的不同 zone,每個 zone 的讀請求實現單元內閉環,寫請求則跨 zone 訪問對應的主庫,需要在資料庫proxy 或者sdk層實現請求路由的自動感知和判斷。
  • 異地多活: 依賴業務單元化改造,讀寫請求全部單元內閉環,要求整體架構具備單元流量排程以及異常流量矯正的能力。屬於同一個 global cluster 的叢集間通過 DTS 進行雙向資料同步,DTS 層需要解決資料迴環問題以及衝突檢測機制,對於資料衝突的場景提供不同的策略,比如覆蓋策略、暫停同步策略等。對於衝突資料也可以考慮寫入訊息佇列,便於業務側矯正處理,同時應具備全域性發號器主鍵填充能力,避免雙向同步的主鍵衝突。
  • 兩地三中心: 可以簡單理解為同城多活加上異地災備架構,其中災備機房應當根據災難承受程度和資料保護程度來權衡地理距離。
上述幾種架構中, 異地災備和同城雙活的讀流量通過資料庫接入層管控進行切流,比如 proxy、sdk的元資料管理,寫流量則以資料庫高可用元件的切換作為切流手段;而異地多活架構下的切流方案一般是通過應用上層切換,比如 CDN、SLB、應用閘道器等實現。  

4、Proxy資料庫代理


資料庫代理(又名proxy)是位於資料庫服務和應用服務之間的網路代理服務,用於代理應用服務訪問資料庫時的所有請求。 資料庫代理一般包含以下核心功能:
  • 讀寫分離
  • 攔截
  • 限流
  • 熔斷
通過對Proxy的大量使用,我們可以實現針對某個資料庫、某個服務、某類 SQL 指紋進行攔截,限流、熔斷來阻止某些異常流量打崩資料的場景。  

5、慢查詢預警


慢查詢是導致資料庫效能下降中少數具有可預測性的內容,為此我們專門建設了慢查詢預警體系,這套體系包含日誌採集、流式處理、結果分析、告警及其他操作(如:自愈等)。 慢查詢預警系統的採集樣本是過去7天同時間段前後十分鐘,和當天前10分鐘資料,通過多次線性迴歸,我們實現了對偶發性的抖動的過濾 ,不同業務級別環比倍數、持續性增長(未到閾值倍數,但持續增長或存在)慢查詢的預警,並且基於規則引擎實現自定義處理。  

6、一個具體的小案例分享


下面分享一個針對複製bug的處理過程。

  • 問題背景:

業務研發收到多起建立預約的問題反饋,如:建立預約後拉取不到預約詳情

查詢從庫未讀取到相關資料

  • 核心處理步驟:

DBA先檢查從庫複製狀態,未發現複製異常;使用show global variables like '%gtid%'檢查gtid,發現gtid_binlog_pos不發生變化,故判斷可能是mariadb多執行緒複製bug導致資料不同步;與業務研發同事溝通後,通過修改proxy配置將所有流量切換到主庫,同時切換canal到主庫,最終因複製bug導致的業務影響很快被控制下來。
  • 後續優化:
  • 增加心跳錶以規避複製bug導致的常規復制監控不可用。
  • 基於高可用、心跳錶、資料庫負載(QPS、CPU、網路等)等資訊,實現在從庫異常情況下將流量切換到主庫和資料訂閱的資料來源切換到主庫。
  • 說明:
觸發該複製bug後,使用show slave status看不到複製狀態的任何異常,下圖是當時的監控:

四、資料庫故障演練應該怎麼做?


故障演練是一種遵循混沌工程實驗原理的解決方案,提供豐富的故障場景模擬(如宕機、CPU負載過高、網路延遲、丟包、磁碟寫滿、I/O飆高、OOM)等,能夠幫助分散式系統提升容錯性和可恢復性。 故障演練需要建立一套標準的演練流程,包含準備階段、執行階段、檢查階段和恢復階段。 資料庫作為基礎軟體,其自身的故障演練不能獨立於應用場景,而是作為應用系統演練的一部分存在。 下面分享下一個故障演練的案例。 B站很多業務的資料庫是Gzone架構, 保證單機房的資料庫、快取是否可以承載業務的全部流量是Gzone架構的核心要求 。下圖是模擬單機房故障演練的架構圖: 此次演練是模擬在同城一個IDC(B機房)因某種不可抗力的情況下,通過CDN將流量切換到A機房,驗證單機房中應用、快取、資料庫是否可以承載全部流量,出現非預期內的情況下現有預案是否可以覆蓋相關場景、相關關聯絡統的可用性等。 最終我們通過模擬check了資料庫切流的效果以及相關預案的覆蓋度、有效性,為系統的穩定性增加了一份保障。由於故障演練這塊的內容比較多,下次我們再開一篇來說說故障演練的流程與實操。 (如果你對B站「故障演練」感興趣,後臺回覆“ 0922” 訂閱相關內容)

五、總結與展望


萬物流變,無物常駐。博爾赫斯 · 赫拉克利說過:沒有人能兩次踏進同一條河流,故障也是類似的,每次故障發生的時間、地點、原因、影響範圍、處理的人都不同。若從這個角度來看故障治理,它是在解決過去某一個時刻的問題,那為什麼還要做故障治理呢? 簡單來說,故障治理的目的就是通過技術手段提前規避類似的故障問題,不讓系統在同一個地方跌倒兩次,而且大量的實踐也證實了這些做法的有效性。 未來B站在資料庫故障治理方面也會持續發力,我們將持續關注資料庫故障標準化、演練常態化。通過常態化的演練更早發現和暴露問題,不斷豐富的故障場景,可以做到最小粒度定義故障、發現故障和處理故障。

回覆【0922】訂閱故障演練內容

回覆【交流】進入讀者交流群

回覆【7161】獲取「B站多活容災建設」資料

宣告:本文由公眾號「TakinTalks穩定性社群」聯合社區專家共同原創撰寫,如需轉載,請後臺回覆“轉載”獲得授權。 更多故障治理內容



「好文推薦」 👉 美圖SRE:一次線上大事故,我悟出了故障治理的3步9招 👉監控告警怎麼搭建比較合理? 👉故障覆盤後的告警如何加出效果? 👉阿里雲彈性計算SRE實踐:億級呼叫量下的預警治理 📢點選【閱讀原文】直達TakinTalks穩定性社群,獲取更多實戰資料!