如此超高的性能,有人會問,阿里云到底用了什么秘密技術(shù)?安全系數(shù)如何?
本文將從冗余設(shè)計思路出發(fā),分享阿里云是如何建立存儲冗余體系,抵御未知風(fēng)險,解決故障率的。
舞動的橋
1940年11月7日,竣工才4個月的塔科馬海峽大橋在微風(fēng)中大幅度舞動,橋上的汽車急速滑動,很快就戲劇性的垮塌。這件事故給建筑工程行業(yè)造成極大的震驚,很違反直覺,很低的風(fēng)速居然會吹垮一座鋼鐵建造的大橋,在此之前所有的橋梁專家都沒有意識到這個問題,事實上若干年后橋梁結(jié)構(gòu)學(xué)與空氣動力學(xué)得到極大發(fā)展,人類才徹底解決這個問題。
在工程領(lǐng)域通常將這類事前缺少認知,只有發(fā)生后才能意識到的問題統(tǒng)稱為險惡性問題,這類問題殺傷力極大,對于盤古這樣的分布式系統(tǒng)同樣會面臨這類險惡性問題,既然對問題都沒有認知,又防御何談呢?陽光之下并無新事,我們從各種安全至關(guān)重要的行業(yè)中廣泛的學(xué)習(xí),發(fā)現(xiàn)各個行業(yè)在面對這類問題時,已經(jīng)有較為豐富的經(jīng)驗了,同樣以橋梁為例,早于的塔科馬海峽大橋的金門大橋(1937年建成通車)已經(jīng)巍然屹立了80多年,可謂飽經(jīng)風(fēng)霜,后人在分析其設(shè)計者留下的設(shè)計手稿時發(fā)現(xiàn)早期的設(shè)計者在面對未知世界時非常謙卑,他們知道有些問題他們搞不清楚,可能會有風(fēng)險,所以他們做了充分的冗余設(shè)計來抵御這種未知的風(fēng)險,通過充分的冗余來斬斷可能形成的故障鏈。
其實存儲是一個高危行業(yè),首先要保障的是不錯,不丟,可訪問,只有在這個前提下,極致的性能才有意義。不錯,不丟,可訪問,看起來平淡無奇,但實踐下來常有如履薄冰之感,因為分布式問題太過復(fù)雜,因為操作系統(tǒng)、硬件都不是絕對可靠的,因為代碼不可能絕對無bug,運維也不可能從不出錯。有太多險惡性問題,即使行業(yè)先行者也出現(xiàn)過數(shù)據(jù)丟失和不可用。在盤古設(shè)計之初我們學(xué)習(xí)了上文中冗余設(shè)計的做法,承認自己所知有限,在很多地方做了冗余設(shè)計來抵御未知風(fēng)險,并且在長期的演進中不但強化這類冗余設(shè)計,絕不為了性能或者其他的目標來做冗余設(shè)計上的妥協(xié)。
這些年來阿里云存儲團隊的同事們始終心存畏懼,鐵棒磨針,始終專注于提供穩(wěn)定可靠高性能的存儲,天道酬勤,10年來我們做到了數(shù)據(jù)不錯,不丟,非常慶幸。
下面我分享幾個阿里云在存儲方面的冗余設(shè)計:
- E2E的數(shù)據(jù)校驗
磁盤可能出錯,內(nèi)存也會出現(xiàn)bit反轉(zhuǎn),內(nèi)核和應(yīng)用程序更可能出錯,甚至我們遇到過某廠的一塊CPU在做某些特定運算時就會出錯,在這樣一堆不可靠的軟硬件環(huán)境下,要構(gòu)建出一個穩(wěn)定可靠的存儲平臺(沒有哪個用戶能接受哪怕一個bit的錯誤),是非常困難的,一般的做法是端到端的逐層做數(shù)據(jù)校驗,盤古很早就做了E2E 的數(shù)據(jù)校驗。但一次在一個長時間大壓力的測試環(huán)境上MySQL 報數(shù)據(jù)錯,核對數(shù)據(jù)發(fā)現(xiàn)CRC和數(shù)據(jù)是匹配的,并沒有出錯,核對另外2份拷貝,發(fā)現(xiàn)另外兩份數(shù)據(jù)也是CRC自洽的,而且另外兩份完全一致,但與MySQL讀取的這一份差異非常大。初步判定是MySQL讀取的這一份數(shù)據(jù)出錯了,但這份錯誤的數(shù)據(jù)是CRC自洽的,這就非常奇怪了,如果是存儲介質(zhì)出錯,不太可能還保持CRC自洽,在讀寫鏈路上查看了所有日志,沒有找到任何異常。調(diào)查的同事反復(fù)核對三份拷貝,說這一份看起來就不像是這個文件的數(shù)據(jù)。
說者無心,聽者有意,另外一個同事想在哪種情況下會出現(xiàn)這樣的錯誤,由于我們的CRC和數(shù)據(jù)是邏輯空間上是相鄰的,會不會是這片數(shù)據(jù)其實不屬于當(dāng)前文件,而是和另外一個文件搞串了?大膽假設(shè),小心求證,全盤掃描后果然證實了猜想:A和B兩個文件中間有一部分內(nèi)容寫串了,詳細調(diào)查后確認是EXT4 文件系統(tǒng)的BUG,它在做空間管理的時候出錯,導(dǎo)致A文件的一個數(shù)據(jù)片寫入到文件B, 文件B中有一個數(shù)據(jù)片寫入到文件A中,由于寫入的流程一致,CRC又和數(shù)據(jù)放置在一起,導(dǎo)致CRC無法發(fā)現(xiàn)這個問題,確認問題后解決起來就很簡單了,要么將CRC和數(shù)據(jù)分開放置,要么在計算CRC的時候加上文件的一個特征值。
有E2E的數(shù)據(jù)校驗是否就安全了呢?呵呵坑深不見底,硬件行業(yè)的人知道磁盤極小的概率發(fā)生靜默錯誤(讀出來的數(shù)據(jù)是錯誤的,但底層接口不報錯,磁盤本身也不報錯),如果三份拷貝很長時間都未被讀取,而在這個較長的時間窗口內(nèi),如果有三份拷貝所在的3塊盤都發(fā)生了靜默錯誤,用戶讀取數(shù)據(jù)時,存儲系統(tǒng)就會發(fā)現(xiàn)3份CRC校驗都不通過,盡管知道出差錯了,卻沒有任何辦法修復(fù)。為了降低這種情況發(fā)生的概率,我們會定期掃描冷數(shù)據(jù),校驗其CRC。
- 壓縮檢驗
一天新來的同事問大家:“我們在做壓縮的時候,為啥壓縮完后,立刻又解壓縮一次,并且將解壓數(shù)據(jù)和原始數(shù)據(jù)再去比對一次?直接壓縮不就完了嗎?”,眾人笑而不語,他思考良久,也未能找到足夠的理由。團隊內(nèi)老司機告訴他,壓縮本身是一個很復(fù)雜的東西,有些庫是第三方提供的,盡管我們會review代碼,有嚴格的引入測試,但沒人能保障其絕對正確,萬一壓縮出來的內(nèi)容是錯的怎么辦?CPU也可能存在一些偶發(fā)性的錯誤,如果壓縮時發(fā)生這類小概率的偶發(fā)性錯誤,該怎么辦呢?但戶數(shù)據(jù)是絕對不能錯的,所以我們這里采取防御性編程,壓縮完后立即解壓,再和原始內(nèi)容比對,確保數(shù)據(jù)不錯。
- PAXOS
說到分布式存儲,很多人就言必稱PAXOS和RAFT,PAXOS儼然就成了救世主。PAXOS, RAFT 的確是好東西,但并不是所有的場景都適用。在一個分布式系統(tǒng)中,大家會認為在一個較小的時間窗口內(nèi)同時發(fā)生2臺機器failover是一個小概率事件,但隨著集群規(guī)模的擴大,集群數(shù)量的增多,最終小概率事件長期積累,就變成必然事件了,在quorum=3的時候,PAXOS 協(xié)議是無法處理double fail的,甚至無法自愈,需要緊急人為干預(yù)才能恢復(fù)。想想半夜收到告警,你能在幾分鐘能處理好用戶的IO HANG? 適合的才是最好的,實踐是檢驗真理的唯一標準。
- 磁盤錯誤
對于做存儲的人來說,磁盤故障是司空見慣的事情了,本身有一套成熟的處理機制。但百密一疏,我們還是在這上面栽過跟頭,進程主動檢測到binary所在的系統(tǒng)盤故障,老司機當(dāng)然知道此時不能再在該盤上發(fā)起任何IO 操作,日志寫入到內(nèi)存中,調(diào)用exit, 安靜的退出,不帶走一片云彩即可。但進程居然無法退出,調(diào)用exit 無法退出!而此時其它線程還在繼續(xù)工作,在干壞事。所有人都覺得匪夷所思,為啥主線程調(diào)用 exit, 進程未能退出,其他線程在長達幾分鐘內(nèi)還在繼續(xù)干壞事?團隊大牛反復(fù)推演,不放過任何一個蛛絲馬跡,終于搞明白了,原來磁盤故障后,exit這段代碼本身并不在內(nèi)存中,調(diào)用時會產(chǎn)生major fault, 中斷處理程序會嘗試從磁盤上load相應(yīng)的代碼段,而磁盤已經(jīng)故障了,load 被hang住,導(dǎo)致出現(xiàn)前面這些匪夷所思的怪事。
今天云計算已經(jīng)成為互聯(lián)網(wǎng)的基礎(chǔ)設(shè)施,隨著眾多電力、水務(wù)、醫(yī)療企業(yè)上云,阿里云又成了這些基礎(chǔ)設(shè)施的基礎(chǔ)設(shè)施,任何一個黑天鵝都有可能帶來難以估量的影響。只有對這個未知的世界保持敬畏,保持謙卑,才能走得更遠。