今天群里有人問(wèn)起,剛好做過(guò)相關(guān)的工作,特此分享一下當(dāng)時(shí)的工作內(nèi)容和感受。
背景
大概說(shuō)一下這個(gè)事情的背景。在2013年大概4月份,人人網(wǎng)打算做一次大規(guī)模的數(shù)據(jù)遷移——評(píng)論服務(wù)。所謂評(píng)論就是指各種資源下的“評(píng)論文字”,比如照片的評(píng)論、Blog的評(píng)論、分享的評(píng)論、音樂(lè)的評(píng)論…… 早期人人網(wǎng)的各個(gè)開(kāi)發(fā)小組各自為政,每個(gè)團(tuán)隊(duì)幾乎都實(shí)現(xiàn)了一個(gè)評(píng)論服務(wù),有各自不同的功能和數(shù)據(jù)結(jié)構(gòu),但是大體上還算相似。當(dāng)時(shí),業(yè)務(wù)部門希望能夠集中這些數(shù)據(jù)做一些統(tǒng)一的管理,比如權(quán)限管理(控制誰(shuí)能看什么評(píng)論)、比如數(shù)據(jù)內(nèi)容推薦(基于用戶評(píng)論人和評(píng)論的內(nèi)容計(jì)算關(guān)系的緊密性等等)。而這一切的基礎(chǔ)是評(píng)論內(nèi)容的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)必須一致。
而同時(shí),UGC這邊的評(píng)論內(nèi)容(數(shù)據(jù)量最大的評(píng)論服務(wù))之前使用Mongo DB開(kāi)發(fā),有很多維護(hù)上的問(wèn)題。MongoDB雖然當(dāng)時(shí)乘著NoSQL的東風(fēng)呼風(fēng)喚雨,但是據(jù)當(dāng)時(shí)的維護(hù)同事講,維護(hù)負(fù)擔(dān)不小,不如MySQL穩(wěn)定。而人人當(dāng)時(shí)具有國(guó)內(nèi)頂級(jí)的MySQL數(shù)據(jù)團(tuán)隊(duì)(順便拜一下劉啟榮、周彥偉兩位大佬)。于是當(dāng)時(shí)高層的意見(jiàn)是將整個(gè)服務(wù)遷移到MySQL架構(gòu)下。
此外,架構(gòu)組當(dāng)時(shí)正在推行新一代的SOA架構(gòu),基于Thrift開(kāi)發(fā)。需要一個(gè)服務(wù)作為“參考實(shí)現(xiàn)”,給整個(gè)公司的研發(fā)團(tuán)隊(duì)做一個(gè)參照。
當(dāng)時(shí)我剛剛工作2年半,進(jìn)入人人的架構(gòu)組不久。在做了一些“沒(méi)有什么人在意”的基礎(chǔ)架構(gòu)后實(shí)在是有些無(wú)聊,就接了這個(gè)活。
問(wèn)題規(guī)模
涉及到的團(tuán)隊(duì)是13個(gè)。
涉及到的總數(shù)據(jù)量大約在10T的量級(jí)。
訪問(wèn)的總量大概在10億PV/日(因?yàn)樵u(píng)論在首頁(yè)feed上就有)。
除了數(shù)據(jù)遷移,這個(gè)項(xiàng)目還需要解決
- 用新的SOA架構(gòu)開(kāi)發(fā)應(yīng)用服務(wù)
- 重新設(shè)計(jì)一個(gè)全新的服務(wù),大致可以照顧13個(gè)團(tuán)隊(duì)的不同的評(píng)論服務(wù)的全部功能(比如點(diǎn)贊、回帖),重新設(shè)計(jì)評(píng)論的基礎(chǔ)服務(wù)
- 與13個(gè)團(tuán)隊(duì)交涉、排期、聯(lián)測(cè)
- 需要與首頁(yè)Feed關(guān)聯(lián),涉及到與C++互操作(因?yàn)镕eed用C++,而其他組大多用Java)
- 整個(gè)開(kāi)發(fā)期間不得中斷服務(wù)
- 大并發(fā)訪問(wèn)量,所以要做嚴(yán)格的壓測(cè)
- 熱門主題的評(píng)論可能多大幾萬(wàn)到十幾萬(wàn)條,分頁(yè)、計(jì)數(shù)都不能用常規(guī)做法實(shí)現(xiàn)
限于主題,本文就不敘述這些設(shè)計(jì)和開(kāi)發(fā)的細(xì)節(jié),只說(shuō)數(shù)據(jù)遷移本身。
數(shù)據(jù)遷移要考慮的問(wèn)題
抱歉廢話了一番才說(shuō)到重點(diǎn)。這里簡(jiǎn)單列舉一些遷移時(shí)要考慮的問(wèn)題。
平滑過(guò)渡
平滑過(guò)渡,即如何做到不同格式數(shù)據(jù)服務(wù)可以在用戶無(wú)感知的情況下做到平滑遷移。答案是雙寫(xiě)和可控讀取路徑。當(dāng)用戶寫(xiě)入評(píng)論時(shí),每個(gè)涉及的要做遷移的服務(wù)在保持寫(xiě)自己的數(shù)據(jù)之外,都必須通過(guò)新版本的SOA客戶端訪問(wèn)新的評(píng)論服務(wù)。

而雙寫(xiě)一旦開(kāi)始,我們就定義了數(shù)據(jù)遷移的時(shí)間范圍。即,只遷移雙寫(xiě)之前的數(shù)據(jù)。注意一點(diǎn),這里因?yàn)橹皇窃u(píng)論,所以少許的雙寫(xiě)不一致(造成用戶評(píng)論丟失)是可以接受的。如果這里遷移的是交易記錄,就得保證絕對(duì)的一致性,那么必須額外開(kāi)發(fā)WAL保證一致性。
而等到數(shù)據(jù)全部遷移完畢,通過(guò)線上配置中心的開(kāi)關(guān),統(tǒng)一切換評(píng)論的讀取路徑,全部落在新的服務(wù)上。這樣就徹底避免了用戶可見(jiàn)的問(wèn)題。

海量數(shù)據(jù)設(shè)計(jì)
10T的數(shù)據(jù)不是小數(shù)目,是4~5年積攢下的數(shù)據(jù)量。對(duì)于新的評(píng)論系統(tǒng),容量設(shè)計(jì)必須將容量設(shè)計(jì)為“3年內(nèi)不需要擴(kuò)容”的。所以設(shè)計(jì)的數(shù)據(jù)量大概是在30T左右。為此,我們?cè)O(shè)計(jì)了partition方案,實(shí)現(xiàn)了100個(gè)虛擬的分庫(kù)。這100各分庫(kù)被動(dòng)態(tài)的分片在10 * 3臺(tái)主機(jī)上。如果未來(lái)性能無(wú)法撐住,可以增加更多的磁盤和更多的主機(jī),最多可以達(dá)到100 * 3臺(tái)主機(jī)的容量。
所謂10 * 3個(gè)主機(jī)是指每組機(jī)器組成一個(gè)3節(jié)點(diǎn)的replica set互為備份。人人的數(shù)據(jù)組有非常強(qiáng)大的工具。在沒(méi)有percona等工具時(shí),這些備份,熱切機(jī)制都是人人數(shù)據(jù)組自己開(kāi)發(fā)的。同時(shí),主從切換等機(jī)制對(duì)業(yè)務(wù)開(kāi)發(fā)都是完全透明的(當(dāng)然,代價(jià)就是業(yè)務(wù)代碼不可以有復(fù)雜SQL,都必須是簡(jiǎn)單的單表SQL)。
partition采用雙重設(shè)計(jì)——主要的partition的id是評(píng)論的資源ID,比如一個(gè)照片的ID,一個(gè)Blog的ID等。因?yàn)?0%的評(píng)論查詢都是查詢一個(gè)資源的評(píng)論列表。這樣partition的好處是使得這樣的查詢總是只會(huì)落到一個(gè)分片上。而部分業(yè)務(wù)提供了“一個(gè)用戶“的評(píng)論列表查詢。所以對(duì)于這樣的數(shù)據(jù),就需要冗余一份數(shù)據(jù)重新做partition——按照”評(píng)論作者ID“進(jìn)行分片。
上面說(shuō)的這些其實(shí)跟數(shù)據(jù)遷移關(guān)系并不大,只不過(guò)在編寫(xiě)遷移數(shù)據(jù)腳本時(shí),必須考慮到這些地方,而非僅僅是簡(jiǎn)單的往一個(gè)數(shù)據(jù)源里插入。
評(píng)論ID
原有的評(píng)論系統(tǒng)數(shù)據(jù)量有大有小。部分?jǐn)?shù)據(jù)的ID簡(jiǎn)單的用auto increment實(shí)現(xiàn),部分系統(tǒng)則簡(jiǎn)單的用uuid,而部分系統(tǒng)使用了全局序列產(chǎn)生器(使用Postgres sequence)。
為了保證新系統(tǒng)的效率,新系統(tǒng)采用snowflake算法來(lái)產(chǎn)生分布式ID,既保證新的數(shù)據(jù)的ID“大致有序”,有利于插入效率,又避免了因入中心化的ID產(chǎn)生機(jī)制。
出錯(cuò)處理
這么浩大的開(kāi)發(fā)過(guò)程,不出錯(cuò)時(shí)完全不可能的。所以必須提前設(shè)計(jì)出錯(cuò)時(shí)如何追蹤錯(cuò)誤。而我們的處理是一定要把一條評(píng)論的新老兩個(gè)ID在新系統(tǒng)都要記錄下來(lái)。一旦發(fā)現(xiàn)數(shù)據(jù)有問(wèn)題,可以立刻反查原始數(shù)據(jù)。
Emoji
新的評(píng)論服務(wù)支持emoji,對(duì)應(yīng)于MySQL的utf8_mb4編碼。在數(shù)據(jù)遷移時(shí)必須留意這一點(diǎn)。當(dāng)年的MySQL中utf8_mb4并不是默認(rèn)編碼,必須經(jīng)過(guò)配置和重啟才可以。這是個(gè)小坑,要留意。
限流
遷移說(shuō)白了就是把老的數(shù)據(jù)讀出來(lái),寫(xiě)入到新的數(shù)據(jù)源。但對(duì)于訪問(wèn)量極大的系統(tǒng),絕對(duì)不能無(wú)節(jié)制的對(duì)數(shù)據(jù)進(jìn)行讀寫(xiě)。任何系統(tǒng)的帶寬都是有限的,磁盤的IO也是有限的,所以一定要限流。我們的遷移腳本在讀取和寫(xiě)入數(shù)據(jù)時(shí)都都會(huì)監(jiān)控所消耗的時(shí)間。如果超過(guò)閾值就開(kāi)始短暫的等待。比如讀取100條數(shù)據(jù)花100ms以上,腳本機(jī)會(huì)停頓1s;如果超過(guò)200ms,腳本就會(huì)停頓5s。超過(guò)一定閾值,腳本會(huì)發(fā)郵件通知我:“遷移暫時(shí)中斷,需要人工重啟”等等。
這樣的限流腳本,一旦開(kāi)啟后,大部分時(shí)間我就不用盯著,不用操心生產(chǎn)系統(tǒng)被壓垮。只用每天花點(diǎn)時(shí)間看看進(jìn)度就行了。
進(jìn)度控制
我們?cè)谶w移的第一周大概估算了大概的遷移速度。然后讓遷移腳本每隔一定的進(jìn)度就將當(dāng)前的已遷移數(shù)據(jù)比例記錄在一個(gè)數(shù)據(jù)表里。因?yàn)樽龅谋容^糙,所以沒(méi)有開(kāi)發(fā)UI,而僅僅是做了個(gè)小腳本每天發(fā)送進(jìn)度郵件給相關(guān)人員。如果有任何異常,都可以引起我們的警覺(jué)。因?yàn)檫@個(gè)工具我們還發(fā)現(xiàn)了幾個(gè)組的程序的bug……
遷移冪等
遷移腳本會(huì)出錯(cuò),而遷移本身是并不是原子的——因?yàn)闃I(yè)務(wù)的復(fù)雜性,我們無(wú)法用transaction。所以,我們的遷移必須是冪等的。我們利用了原始數(shù)據(jù)的服務(wù)名稱+ID作為冪等key(比如PHOTO:12345),以及INSERT IGNORE INTO,配合進(jìn)度控制可以實(shí)現(xiàn)簡(jiǎn)單的重啟數(shù)據(jù)遷移。這樣即便遷移腳本被強(qiáng)行的kill掉,同時(shí)又沒(méi)有記錄下精確的動(dòng)作。我也可以放心大膽的隨意重啟它。
如果原來(lái)的評(píng)論數(shù)據(jù)變了……
盡管理論上評(píng)論時(shí)不會(huì)變的,但是有些業(yè)務(wù)的評(píng)論的確可以編輯(盡管數(shù)量很?。_@樣在遷移過(guò)程中,我們不得不考慮怎么去重新同步這些變化。我用了最簡(jiǎn)單的辦法,對(duì)每條記錄的updated_at做索引和排序。感謝公司良好的數(shù)據(jù)表設(shè)計(jì)規(guī)范,每個(gè)表都有created_at和updated_at(或者created_on和updated_on,大家英文不統(tǒng)一,但只要有就足夠了)。一旦發(fā)現(xiàn)新的數(shù)據(jù)變更,就排在一個(gè)隊(duì)列里進(jìn)行特別的同步。這解決了絕大部分的問(wèn)題。還是那句話,好在是評(píng)論,不需要特別嚴(yán)格一致,所以就算是丟了那么幾條的改動(dòng),也是可以接受的。
激勵(lì)和進(jìn)度管理
這個(gè)其實(shí)是整個(gè)項(xiàng)目里最難的。因?yàn)槲沂且粋€(gè)普通的研發(fā)。雖然我來(lái)自架構(gòu)組,但是這并不代表別的組的人會(huì)按我所得做。每個(gè)組都有自己的KPI,有自己的排期和優(yōu)先級(jí)。我想把我的工作目標(biāo)插入到他們的安排中,真是各種招都用了。有的普及技術(shù)、有的找項(xiàng)目負(fù)責(zé)人談合作、還有的直接吃飯KTV。但不論如何,在相對(duì)短的時(shí)間內(nèi),這項(xiàng)工作真真切切的落實(shí)了。現(xiàn)在想想覺(jué)得還是很NB。
另外這么多事情,包括數(shù)據(jù)設(shè)計(jì)、功能開(kāi)發(fā)、聯(lián)測(cè)、壓測(cè)、不同組的溝通排期、數(shù)據(jù)遷移、切換開(kāi)關(guān)在13個(gè)組里進(jìn)行?,F(xiàn)在想想,目前的項(xiàng)目管理和文檔能力都是在那時(shí)候鍛煉成的。
最后
經(jīng)過(guò)2個(gè)半月的遷移和開(kāi)發(fā),這個(gè)事情終于告一段落。業(yè)務(wù)的頭頭們得到了統(tǒng)一的評(píng)論數(shù)據(jù),用戶沒(méi)有罵娘,架構(gòu)組的SOA基礎(chǔ)框架也有了第一個(gè)使用樣板(其實(shí)我被坑了好幾次,所以架構(gòu)組也沒(méi)少請(qǐng)我吃飯撫慰我的心靈)。通過(guò)這個(gè)過(guò)程也得到了一幫好哥們,和第一次季度S績(jī)效(后邊還順便升了個(gè)title,但是沒(méi)過(guò)多久就離開(kāi)了人人)。我很感謝這個(gè)經(jīng)歷和幫助我的團(tuán)隊(duì)。
收一收,通過(guò)這件事情總結(jié)一下關(guān)于數(shù)據(jù)遷移的重點(diǎn):
- 精心的進(jìn)度管理和控制
- 開(kāi)發(fā)“低心智負(fù)擔(dān)”的工具
- 平滑過(guò)渡,讓用戶開(kāi)心和滿意
最后說(shuō)一句:做業(yè)務(wù)真心容易出績(jī)效啊!