2021年Roblox史上最長宕機事件:73小時大停機背後的技術債務
2021年10月,Roblox遭遇了史上最長的宕機事件,整整73小時的完全停機,影響了全球數百萬玩家。根本原因是什麼?一個隱藏在過時數據庫結構深處的微妙但毀滅性的問題。令人著迷的是,這個問題不是明顯的系統故障或外部攻擊,而是一個隱藏在數據庫中的慢燃技術債務。
最近,我深入研究了Roblox的 事後分析報告,並獲得了寶貴的見解。這可以說是線上最詳細和最徹底的宕機報告之一。
### 背景
Roblox採用微服務架構作為後端,使用 HashiCorp 的 Consul 進行服務發現,允許內部服務彼此定位和通信。10月28日下午,一台Consul伺服器出現高CPU負載。Consul集群的性能持續下降,最終導致整個Roblox系統癱瘓,因為Consul成了一個 單點故障。Roblox和HashiCorp的工程師共同合作,診斷和解決了問題。
### 除錯嘗試
1. 疑似硬體故障:團隊更換了一個Consul集群節點,但問題仍然存在。
2. 疑似流量增加:團隊更換了所有Consul集群節點,升級為更強大的機器,配備128個核心(2倍增加)和更快的NVMe SSD磁碟。這也沒有解決問題。
3. 重置Consul狀態:團隊關閉了整個Consul集群,並使用宕機前幾個小時的快照恢復其狀態。最初,系統似乎穩定,但很快又惡化,恢復到不健康狀態。
4. 減少Consul使用:通常運行數百個實例的Roblox服務被縮減到個位數。這種方法提供了幾個小時的暫時緩解,然後Consul再次變得不健康。
5. 識別資源爭用問題:在深入檢查調試日誌後,團隊發現了資源爭用問題。他們恢復到宕機前使用的類似機器。最終,他們確定了問題:Consul的新流媒體功能。該功能使用了較少的並發控制元素(Go通道),導致在高讀寫負載下單個Go通道出現過度爭用。禁用流媒體顯著改善了Consul集群的健康狀況。
6. 領導者選舉優化:團隊觀察到Consul間歇性地選舉新的集群領導者,這是正常的。然而,一些領導者表現出相同的延遲問題。團隊通過防止有問題的領導者繼續被選舉來解決這個問題。
✅ 經過這些措施,系統終於穩定了。團隊通過恢復緩存系統並逐漸允許隨機選擇的玩家重新連接,小心地讓Roblox恢復上線。 73小時後,Roblox恢復了正常運行。
### BoltDB的Freelist:一個沉默的罪魁禍首
宕機的根本原因源於兩個關鍵問題: Consul的流媒體功能 (如上所述)和 Consul底層數據庫BoltDB 出現嚴重的性能下降,這一點我覺得相當有趣。
Consul使用Raft共識演算法進行領導者選舉,以確保分散式環境中的數據一致性。為了持久化,它依賴於BoltDB,一個流行的嵌入式鍵值存儲,來存儲Raft日誌。
像許多數據庫一樣,BoltDB有一個 freelist ,它跟踪 空閒頁面 ——以前佔用但現在可重用的磁碟空間。這個機制對於高效的數據庫性能至關重要,可以防止不必要的磁碟增長並優化讀寫操作。
然而,BoltDB的freelist實現有一個關鍵的低效(見 源代碼 )。它使用數組來存儲每個空閒頁面的ID,這意味著每個數據庫讀寫操作都涉及freelist的線性掃描。隨著freelist的增長,操作成本顯著增加。
📈 有趣的是,這個性能問題最早在2016年被報告( GitHub問題 ),但從未得到修復。BoltDB的作者Ben Johnson在2017年停止維護該項目,稱:
> 維護開源數據庫需要巨大的時間和精力。對代碼的更改可能會產生意想不到的、有時甚至是災難性的影響,因此即使是簡單的更改也需要數小時的仔細測試和驗證。不幸的是,我不再有時間或精力繼續這項工作。Bolt處於穩定狀態,已經有數年的成功生產經驗。因此,我認為將其保持在當前狀態是最明智的選擇。
儘管BoltDB不再被維護,但Go社區將其分叉成一個名為bbolt的新項目( bbolt GitHub ),以繼續積極維護和添加新功能。然而,Consul仍然使用過時、未維護的BoltDB版本。
2019年,freelist性能問題終於在bbolt中得到解決( 阿里雲博客 )。修復非常簡單:使用哈希表代替數組,減少線性掃描開銷,實現即時查找。( 🚀 我喜歡一個簡單的想法如何帶來巨大的性能提升!)
由於這個修復是在bbolt中提交的,而不是BoltDB,因此Consul並未受益於此改善,最終導致2021年Roblox發生了三天的宕機。
### 未回答的問題
這篇文章涵蓋了很多內容,但仍有許多問題未能解答。作為一名工程師,我發現自己渴望探索更多細節。幾個有趣的問題仍然未能解答:
* 為什麼Roblox沒有更早地回滾Consul的流媒體功能?
鑑於Consul明顯是罪魁禍首,而且最近對其基礎設施進行了重大更改,應該將回滾作為首先嘗試的措施之一。是什麼因素延遲了這一決策?
* 為什麼只有一些Consul伺服器出現了BoltDB freelist性能問題?
理論上,所有伺服器應該處於類似的狀態,因為領導者通常只比其跟隨者領先一點。 然而,只有一些實例出現了嚴重的性能下降。是什麼原因造成了這種不一致?
* 為什麼使用以前的快照恢復Consul狀態無法修復問題?
我的假設是,恢復Consul的狀態並沒有重置每個伺服器上的底層raft.db文件,這意味著即使在回滾後,膨大的freelist仍然存在。 如果是這樣,這表明快照不包括內部數據庫結構的關鍵優化。
* 為什麼在再次失敗之前,減少Consul使用量可以暫時解決問題?
如果freelist已經太大,減少使用量不應該提供任何緩解。 團隊是否暫時減緩了freelist的增長,推遲了不可避免的結果,還是另有其他因素在起作用?
* 為什麼新的流媒體功能在宕機發生前一天運行正常?
如果新的Consul流媒體功能本身就有缺陷,為什麼系統沒有立即崩潰? 是否有一個初始緩衝器暫時掩蓋了問題,或者特定的流量模式觸發了崩潰?
* 既然BoltDB的freelist性能問題已經存在多年,為什麼Roblox以前沒有遇到過這種系統性能下降?
BoltDB的freelist低效性自2016年以來就是一個已知的問題。 Roblox的工作負載或數據結構發生了什麼變化,使得這個問題現在浮現? 新的Consul流媒體功能是否通過顯著增加寫操作到BoltDB而加劇了問題?
### 總結
這份事後分析報告提供了無價的教訓,我高度推薦大家閱讀! 還有關於這次宕機的廣泛討論在 Hacker News 上,BoltDB的作者Ben Johnson也參與了討論。
作為軟體工程師,我堅信 偉大的工程師的一個關鍵特質是能夠高效地導航大型複雜系統,並在壓力下診斷問題。 我對Roblox和HashiCorp的工程師們表示深深的欽佩和尊重,他們在巨大的壓力下不知疲倦地調查和解決了問題。 向他們的韌性和專業知識致敬。
作為一名編輯,我對這篇文章有以下的想法和評論:
Roblox的宕機事件是一個典型的技術債務導致的問題。技術債務是指在軟體開發過程中,為了快速實現目標而採取的捷徑或不完美的解決方案,這些債務可能會在未來導致更大的問題。這個案例中,BoltDB的freelist性能問題就是一個長期的技術債務,它最終導致了Roblox的宕機。
這個案例也凸顯了系統設計和架構的重要性。Roblox採用微服務架構,使用Consul進行服務發現,這使得系統更加複雜,也增加了單點故障的風險。
此外,這個案例也說明了測試和驗證的重要性。如果Roblox的工程師們在實施新功能之前進行更徹底的測試和驗證,也許就可以避免這次宕機。
最後,這個案例也提醒我們,技術債務需要及時償還。即使是小問題,如果不及時解決,也可能會在未來導致更大的問題。因此,軟體開發團隊需要重視技術債務,及時償還,以確保系統的穩定性和可靠性。