聊聊這個讓騰訊云丟數(shù)據(jù)的“靜默損毀”
剛看到一則新聞,說是騰訊云丟了某個客戶的數(shù)據(jù),原因是硬盤bug導(dǎo)致“寫進(jìn)去的數(shù)據(jù)讀出來并不是之前寫入的數(shù)據(jù)”,當(dāng)然,不管具體是不是這個原因,詳情如何,不做評論。本次冬瓜哥就來聊聊這個數(shù)據(jù)靜默損毀(silent corruption)的一系列底層知識。
本文就是對靜默損毀做簡要總結(jié)性介紹。靜默損毀大概有幾種方式:
parity error 每個扇區(qū)都會有ecc校驗(yàn)區(qū),硬盤寫入數(shù)據(jù)之前會計算ecc,并在讀出數(shù)據(jù)之后自行校驗(yàn)。按理說這樣應(yīng)該不會靜默損毀?不是的。如果host端發(fā)給硬盤的數(shù)據(jù)已經(jīng)是錯的了,那么硬盤就不會知道。所以,人們使用DIF(Data Integrity Field)來實(shí)現(xiàn)上層的校驗(yàn),也就是說,硬盤上位角色(比如HBA,或者應(yīng)用)主動校驗(yàn)數(shù)據(jù)并將校驗(yàn)碼寫入另外的8字節(jié)中,隨著512字節(jié)的扇區(qū)數(shù)據(jù)一起下發(fā)給硬盤。
DIF中可以完全自定義,也可以按照T10標(biāo)準(zhǔn),DIF中放置扇區(qū)號、校驗(yàn)碼和應(yīng)用自定義信息。為何要放扇區(qū)號?這個下面再說。但是即便是有DIF,也無法保證從應(yīng)用生成數(shù)據(jù),到數(shù)據(jù)寫入硬盤一整條路徑上都不出錯,有些廠商也在致力于從數(shù)據(jù)一生成的時候就時刻跟著校驗(yàn),這個可以在應(yīng)用層來透明的做。
2. paritial write。這個現(xiàn)象是由于硬盤在寫入數(shù)據(jù)時,只寫了一部分扇區(qū)數(shù)據(jù),而另一部分沒有寫入。硬盤一般會保證扇區(qū)粒度的原子寫,但是由于種種不可知因素,這種partial write也會發(fā)生,最終讀出數(shù)據(jù)時多半會發(fā)現(xiàn)校驗(yàn)出錯,從而報告。
此時上層程序可以從副本中讀出正確的數(shù)據(jù),多個副本同時出錯的概率非常低。這個不屬于靜默損毀。在Raid系統(tǒng)里,一個條帶沒有完整被寫完前就掉電了,也稱為partial write,這個可以通過日志或者標(biāo)記條帶完整性來解決,不是什么大問題。
3. write lose。這個現(xiàn)象是說硬盤本該寫入某個扇區(qū),但是最終根本沒有寫入,目標(biāo)扇區(qū)數(shù)據(jù)依然是老數(shù)據(jù)。這個現(xiàn)象會導(dǎo)致靜默損毀,導(dǎo)致應(yīng)用讀出了舊數(shù)據(jù),或者其它應(yīng)用之前保存的完全不相關(guān)的數(shù)據(jù),直接現(xiàn)象肯能是亂碼之類。
這個問題可以通過在應(yīng)用層做標(biāo)記的方式解決,比如應(yīng)用給每個數(shù)據(jù)塊記錄一個時間戳,如果發(fā)生了lose,則時間戳一定對不上,于是就可以判斷出來。這些應(yīng)用層標(biāo)記可以記錄在DIF 8字節(jié)里的應(yīng)用自定義區(qū)域。除了數(shù)據(jù)庫這種對一致性要求非常完備的系統(tǒng),其他應(yīng)用一般不會這么嚴(yán)格,所以一旦發(fā)生這個問題,只能事后恢復(fù),比如從多個副本中再提起一遍數(shù)據(jù)做比對。無法做到事前預(yù)防。
4. mis-redirect write。這個現(xiàn)象是硬盤本應(yīng)寫入A扇區(qū),卻由于不可知原因?qū)戝e了地方,寫到其他扇區(qū)去了。這個問題的原因可能是host端傳的扇區(qū)地址指針中某個或者某幾個bit發(fā)生了畸變(比如SRAM中的互鎖晶體管受到各種電離輻射粒子流轟擊,直接導(dǎo)致狀態(tài)改變)。
這種靜默損毀的后果更嚴(yán)重,不但本次I/O對應(yīng)的扇區(qū)沒被更新,而且還破壞了其他扇區(qū)。這種損毀,也需要靠DIF中的應(yīng)用自定義區(qū)段才能解決,但是這個代價太高昂,因?yàn)閼?yīng)用每讀出一個數(shù)據(jù)塊就要做DIF判斷。