余晟:讓自家系統(tǒng)癱瘓,這事我也干過
最近幾天國(guó)內(nèi)互聯(lián)網(wǎng)圈接連出現(xiàn)停服事故,先是支付寶停服超過 2 小時(shí),然后是攜程停服超過 8 小時(shí)。一時(shí)間許多人大驚:原來大玩家們的運(yùn)維水平這么出乎意料。
我想說的是,我認(rèn)識(shí)的做技術(shù)的,許多人都有把網(wǎng)站(系統(tǒng))搞癱瘓的經(jīng)歷。有些聚會(huì)上,說不出自己把系統(tǒng)搞崩潰過的經(jīng)歷,人家都不認(rèn)為你是“自己人”。還好,我是可以榮列“自己人”隊(duì)伍的,下面我要說的就是我的經(jīng)歷。
2007 年,我當(dāng)時(shí)在“抓蝦網(wǎng)”做后臺(tái)開發(fā)。網(wǎng)齡老一點(diǎn)的讀者大概會(huì)記得這個(gè)網(wǎng)站,當(dāng)時(shí)排名第一的中文在線 RSS 閱讀器。如果不理解什么是“RSS 閱讀”,可以理解為“博客的朋友圈”:當(dāng)時(shí)還沒有微信,只有博客,在線 RSS 閱讀就是把你關(guān)注各博客的更新都抓回來,拼成“朋友圈”方便閱讀的工具。
問題出現(xiàn)在一天晚上。有一張數(shù)據(jù)庫(kù)的表里記錄了當(dāng)前等待執(zhí)行的任務(wù),通常來說,里面的記錄應(yīng)該不超過 100 條。不幸那天程序出了點(diǎn)問題,到下午 6 點(diǎn)堆積了兩三千條記錄,我一直調(diào)到晚上 8 點(diǎn)多才算解決,看著數(shù)據(jù)量一點(diǎn)點(diǎn)降下去,我放心地回家了。
到 11 點(diǎn)半,洗漱完畢,馬上要上床睡覺了,我想登上服務(wù)器確認(rèn)問題已經(jīng)解決。檢查發(fā)現(xiàn),還有不到 200 條記錄??磥韱栴}確實(shí)已經(jīng)解決了,只是看著這個(gè)數(shù)字確實(shí)有些不爽,平時(shí)都不到 100 呢。于是我想,不如把表給清空了吧,數(shù)字就好看了,反正都是會(huì)反復(fù)執(zhí)行的臨時(shí)記錄,也沒什么影響。
等我輸入清空數(shù)據(jù)庫(kù)表的 truncate 語句之后,稍微感覺有些異樣,怎么執(zhí)行了這么久,平時(shí)不用 1 秒的語句,現(xiàn)在竟然執(zhí)行了 5 秒。再看看監(jiān)控系統(tǒng),瞬間大驚失色,瞌睡全無:原來我把最重要的博客地址列表給清空了,抓取、分析、存儲(chǔ)模塊群龍無首,都在空轉(zhuǎn)。
與 delete from 相比,用 truncate 語句清空,速度很快,但無法通過日志來恢復(fù);雖然數(shù)據(jù)庫(kù)有熱備,但可以想象,1 秒不到的時(shí)間里,這條錯(cuò)誤的指令已經(jīng)發(fā)送到各臺(tái)熱備機(jī),所以大家都清空了這張表……
我當(dāng)時(shí)剛剛工作不久,而且一直做后臺(tái)開發(fā),從來沒有遇到過這樣程度的在線問題。第一反應(yīng)就是打電話給領(lǐng)導(dǎo),于是我撥通了振宇(現(xiàn)任去哪兒網(wǎng)集團(tuán)執(zhí)行 副總裁&無線事業(yè)群 CEO)的電話。振宇聽完我的描述,只平靜地說:趕緊恢復(fù)數(shù)據(jù),最遲明天早上必須要解決,不要讓用戶感覺到。
我本來是想讓他告訴我怎么處理,結(jié)果他只給了我一個(gè)目標(biāo)。后臺(tái)系統(tǒng)平時(shí)都是我在維護(hù),找其它同事也幫不上忙。所以我雖然滿頭大汗,也只能努力靜下心來,想想到底要怎么辦。
首先我把相關(guān)的模塊都停下來,避免弄得自己心煩意亂。然后把數(shù)據(jù)庫(kù)的表都畫在紙上,看看互相之間的關(guān)聯(lián),能不能拼一份數(shù)據(jù)出來。很快我找到了從幾張 表拼出原始數(shù)據(jù)的方案(這時(shí)候才知道冗余設(shè)計(jì)是多么好)。寫了一個(gè) Python 腳本測(cè)試,通過。那么趕緊上線!這時(shí)候是凌晨 1 點(diǎn)。
然而事情并沒有那么簡(jiǎn)單,數(shù)據(jù)倒是恢復(fù)出來了,速度慢得嚇人。一共有五百多萬條數(shù)據(jù)要恢復(fù),每秒鐘只能恢復(fù)不到 50 條數(shù)據(jù),全部恢復(fù)要超過 1 天,肯定會(huì)嚴(yán)重影響客戶體驗(yàn)的。
雖然已經(jīng)接近 2 點(diǎn)了,但我睡意全無(也不敢有)又束手無策。只能一邊徒勞地看著恢復(fù)進(jìn)度,一邊分析:如果沒有冷備(當(dāng)時(shí)確實(shí)沒有),看來全部恢復(fù)是得 1 天了。怎么能不讓用戶發(fā)現(xiàn)呢?出個(gè)維護(hù)通告?……忽然我想到,既然用戶訂閱數(shù)都在,先把活躍用戶訂閱的、訂閱數(shù)量多的博客地址挑出來,然后再恢復(fù)其它的。
很快我統(tǒng)計(jì)出符合條件的記錄,大概六十萬條,按照每秒 50 條的速度,一小時(shí)可以恢復(fù) 18 萬條,4 小時(shí)左右可以全部恢復(fù)完畢?,F(xiàn)在不到 3 點(diǎn),全部恢復(fù)完畢是早上 7 點(diǎn)左右,用戶的第一個(gè)活躍期在早上 8 點(diǎn)多(當(dāng)時(shí)絕大多數(shù)人都是 PC 上網(wǎng)),應(yīng)該來得及。
再寫程序,測(cè)試,通過,再上線,觀察了一段時(shí)間,發(fā)現(xiàn)速度是正常的,符合預(yù)期。這時(shí)候已經(jīng)四點(diǎn)多,全身濕透了。我給振宇發(fā)了個(gè)短信說“已經(jīng)在恢復(fù)了,確保不會(huì)影響客戶體驗(yàn)”,才倒下去睡覺……
天亮了,起床第一件事就是檢查恢復(fù)進(jìn)程,發(fā)現(xiàn)沒有問題。然后我懷著忐忑的心情,裝做沒事人一樣去上班,果然沒有人發(fā)現(xiàn)問題。中午吃飯的時(shí)候我偷偷找 到運(yùn)維的同事,說了這件事情。他的反應(yīng)更讓我出乎意料:這有啥,大家都有采坑的時(shí)候,我以前在百度,迷糊了把幾個(gè)T的前端緩存刪掉了,出去抽顆煙,回來該 怎么樣還怎么樣……
到當(dāng)天下午,沒有人訂閱的數(shù)據(jù)也已經(jīng)恢復(fù)了,整個(gè)過程還算成功,沒有用戶發(fā)現(xiàn)異樣。
后記
這次事件給我的印象太深刻了,尤其是大半夜驚心動(dòng)魄地調(diào)試,說折陽壽真是不夸張。所以在這之后,我把冷備做起來了,把備份恢復(fù)方案做起來了,把數(shù)據(jù) 庫(kù)分賬號(hào)控制做起來了,再也不敢隨便清空數(shù)據(jù)表了……吃一塹長(zhǎng)一智,好的系統(tǒng)都是一個(gè)個(gè)小坑填出來的,重要的是看到小坑就要去甜,不能永遠(yuǎn)靠人力處理,靠 手工搏斗。長(zhǎng)期來看,我改變了工作習(xí)慣,每次接手生產(chǎn)系統(tǒng),第一時(shí)間會(huì)去關(guān)注系統(tǒng)的完整和安全,不再著迷于系統(tǒng)架構(gòu)和代碼質(zhì)量。
現(xiàn)在再回想,還有兩點(diǎn)慶幸:一是抓蝦是面對(duì)中文用戶的服務(wù),后來面對(duì)要支持多國(guó)協(xié)作的系統(tǒng)時(shí),才真正知道“全球化”意味著什么;二是振宇當(dāng)時(shí)只設(shè)定 了目標(biāo),給了我鍛煉和成長(zhǎng)的機(jī)會(huì)(后來和新浪談技術(shù)合作,他給我發(fā)的短信只有一句話“今天一定要把新浪的人搞定”,我印象也很深刻)。
當(dāng)然,對(duì)我來說,最重要的收獲是一條終身受用的道理:哪怕情況再緊急,也不能手足無措,靜下來心來仔細(xì)分析,這才是解決問題的根本途徑。有經(jīng)驗(yàn)和沒有經(jīng)驗(yàn)的差別,很多時(shí)候不在專業(yè)技術(shù)上,而在于把握能力上。