資料庫管理系統在今天已經是軟體的重要組成部分,開源的 MySQL、PostgreSQL 以及商業化的 Oracle 等資料庫已經隨處可見,幾乎所有的服務都需要依賴資料庫管理系統存儲數據。
資料庫不會丟失數據聽起來像是理所當然的事情,持久化能力也應該是資料庫的最基本保障,但是在這個複雜的世界上想要保證數據不丟失是很困難的。在今天,我們能找到很多資料庫出現問題導致數據丟失的例子:
無論是開源資料庫還是雲服務商提供的服務,都有可能發生數據丟失的。本文將資料庫丟失數據的原因歸結到以下的幾個方面,我們將詳細展開介紹這些原因:
人為錯誤是造成數據丟失的首要原因。在騰訊雲數據丟失事故中,我們會發現,雖然事故的起因是硬體故障,但是最終導致數據完整性受損的還是運維人員的不當操作:
第一是正常數據搬遷流程默認開啟數據校驗,開啟之後可以有效發現並規避源端數據異常,保障搬遷數據正確性,但是運維人員為了加速完成搬遷任務,違規關閉了數據校驗;
第二是正常數據搬遷完成之後,源倉庫數據應保留24小時,用於搬遷異常情況下的數據恢復,但是運維人員為了儘快降低倉庫使用率,違規對源倉庫進行了數據回收。
減少人為錯誤的最好方式是將數據的備份和運維等操作標準化,使用自動化的流程處理涉及數據安全的操作,這樣才能降低人為干預帶來的風險。
對於軟體工程師來說,我們應該敬畏生產環境,謹慎地在生產環境執行一切操作,認識到所有的操作都可能對線上正在運行的服務產生影響,這樣才能降低類似問題發生的機率。
任何一個線上的服務能夠正常運行都是極其偶然的,只要時間拉的足夠長,我們就沒有辦法保證服務 100% 的可用性[^4]。
磁碟等硬體如果使用的時間足夠長,很有可能會發生損壞,根據 Google 論文中的數據,5 年內硬碟的年平均故障率(Annualized Failure Rates,AFR)為 8.6%[^5]。
2018 年,騰訊雲數據損壞事故的起因就是磁碟靜默錯誤(Silent data corruption)[^6]導致的單副本數據錯誤。磁碟靜默錯誤是沒有被磁碟固件或者宿主作業系統發現的錯誤,包括以下情況:電纜鬆了、電源供給不可靠、外部震動、網絡引起的數據丟失等問題。
正是因為磁碟的數據損壞非常常見,所以我們需要數據冗餘的方式保證磁碟在發生不可修復讀錯誤(Unrecoverable Read Error)時能夠恢復磁碟數據。
獨立冗餘磁碟陣列(Redundant Array of Independent Disks,RAID)是一種能夠將多個物理磁碟組合成一個邏輯磁碟的數據存儲虛擬化技術,它能夠增加數據冗餘並提高性能[^7]。
raid-strategy
RAID 主要使用分割(Striping)、鏡像(Mirroring)和奇偶校驗(Parity)三大策略管理磁碟中的數據,我們這裡舉幾個簡單的例子:
RAID 使用的分割和鏡像策略與分布式資料庫中的分片(Partition)和副本(Replication)比較相似,分割和分片將數據切分後分配到不同的磁碟或者機器,而鏡像和副本的作用都是複製數據。
很多現代的作業系統都會提供基於軟體的 RAID 實現,一些雲服務廠商也會使用自研的文件系統或者冗餘備份機制:
硬體錯誤在生產環境中很常見,我們只有通過數據冗餘和校驗才能降低數據丟失的可能性,但是增加冗餘的方式也只能不斷降低數據丟失的機率,不能 100% 的避免。
資料庫管理系統最終會將數據存儲在磁碟上,對於很多資料庫來說,數據落到磁碟上就意味著持久化完成了。磁碟作為資料庫系統的下層,磁碟能夠穩定存儲數據是資料庫能夠持久化數據的基礎。
database-and-disk
很多人都誤認為使用 write 就能將數據寫入到磁碟上,然而這是錯誤的。函數 write 不僅不能保證數據寫入磁碟,有的實現甚至都不能保證目標空間保留給了寫入的數據[^10]。
一般情況下,對文件的 write 只會更新內存中的頁緩存,這些頁緩存不會立刻刷入磁碟,作業系統的 flusher 內核線程會在滿足以下條件時將數據落盤[^11]:
如果我們想要將數據立刻刷入磁碟,就需要在執行 write 後立刻調用 fsync 等函數[^12],當 fsync 等函數返回後,資料庫才會通知調用方數據已經成功寫入。
write-and-fsyn
write 和 fsync 在資料庫管理系統中非常重要,它們是提供持久性保證的核心方法,一些開發者對 write 的理解錯誤寫出錯誤的代碼就會導致數據丟失。
除了持久化的特性之外,資料庫可能還需要提供 ACID(Atomicity, Consistency, Isolation, Durability)或者 BASE(Basically Available, Soft state, Eventual consistency)的保證,有些資料庫還會提供分片、副本以及分布式事務等複雜功能,這些功能的引入也增加了資料庫系統的複雜性,而隨著程序複雜性的增加,出現問題的可能性也隨之增長。
資料庫管理系統是軟體工程中最複雜、最重要的系統之一,幾乎所有服務的正常運行都建立在資料庫不會丟失數據的假設上。
然而因為如下所示的原因,資料庫不能完全保證數據的安全:
一旦發生數據丟失的事故,造成的影響就會非常大,我們在使用資料庫存儲核心業務數據時也不能完全信任資料庫的穩定性,可以考慮使用熱備以及快照等方式容災。
到最後,我們還是來看一些比較開放的相關問題,有興趣的讀者可以仔細思考一下下面的問題:
作者:Draveness
來源:https://mp.weixin.qq.com/s/Wo5GkfE8VtXTiyL9Kn4LlQ