談?wù)勀澳盃幇栽跀?shù)據(jù)庫方面踩過的坑(前篇)
陌陌爭霸 這個(gè)項(xiàng)目一開始不叫這個(gè)名字,它在 2013 年中的時(shí)候,還只是一個(gè)我們公司 用來試水移動(dòng)游戲的試驗(yàn)項(xiàng)目。
最開始的目標(biāo)很明確,COC 是打動(dòng)我的第一款基于移動(dòng)平臺(tái)網(wǎng)絡(luò)游戲,讓我看到了和傳統(tǒng) MMO 不同的網(wǎng)絡(luò)游戲設(shè)計(jì)方向。我覺得只需要把其中最核心的部分剝離出來,我們很快可以做出一個(gè)簡單的卻不同于以往 MMO 的游戲,然后就可以著手在此基礎(chǔ)上發(fā)展。
至于后來找到陌陌合作,是個(gè)機(jī)緣巧合的故事。
我們的試驗(yàn)項(xiàng)目完成,卻沒想好怎么推給玩家去玩(而這類游戲沒有一定的玩家群體基本玩不起來),而陌陌 游戲平臺(tái)剛上線,僅有的一款產(chǎn)品(類似泡泡龍的游戲)成績不佳。因?yàn)槲覀児竞湍澳暗膭?chuàng)始人都曾經(jīng)在網(wǎng)易工作,非常熟悉。這款游戲也就只花了一個(gè)月時(shí)間就 在陌陌游戲平臺(tái)發(fā)布了。
一開始我們只把剛完成狂刃 啟動(dòng)器項(xiàng)目的阿楠調(diào)過來換掉我來做這個(gè)項(xiàng)目,我在做完了初期的圖形引擎工作后,就把游戲的實(shí)現(xiàn)交給了他。我們只打算做客戶端,因?yàn)橹挥羞@部分需要重新積累技術(shù)經(jīng)驗(yàn);而服務(wù)器不會(huì)和傳統(tǒng) MMO 有太大的不同。而我們公司已經(jīng)圍繞 skynet 這套服務(wù)器框架開發(fā)有很長一段時(shí)間了,隨時(shí)都可以快速把這個(gè)手游項(xiàng)目的服務(wù)器快速搭建起來。
到 2013 年夏天,感覺應(yīng)該開始動(dòng)手做服務(wù)器部分了。曉靖在斗羅大陸的端游項(xiàng)目中積累了不少服務(wù)器開發(fā)的經(jīng)驗(yàn),也是除我之外,對(duì) skynet 最為熟悉的人;如果這個(gè)試驗(yàn)項(xiàng)目只配備一個(gè)程序來開發(fā)服務(wù)器的話,沒有更好的人選了。
從那個(gè)時(shí)候起,我們開始考慮服務(wù)器的結(jié)構(gòu),其中也包括了數(shù)據(jù)庫的選型和構(gòu)架。
skynet 有自己的 IO 模型,如果要足夠高效,最好是能用 skynet 提供的 socket 庫自己寫 DB 的 driver 。因?yàn)?redis 的協(xié)議最簡潔,所以最先我只給 skynet 制作了 redis 的 driver 。而我們代理的游戲狂刃的開發(fā)方使用的是 MongoDB ,為了運(yùn)營方便,我們的平臺(tái)也使用它做了不少東西,我便制作給 skynet 制作了初步的 mongodb driver 。到服務(wù)器開始開發(fā)時(shí),我們有了兩個(gè)選擇。
十多年的游戲行業(yè)從業(yè)經(jīng)驗(yàn)告訴我,數(shù)據(jù)庫在實(shí)時(shí)交互性較強(qiáng)的在線游戲中,主要起的是一個(gè)數(shù)據(jù)備份容災(zāi)的作用。很少會(huì)將其用于數(shù)據(jù)交換。而在其它領(lǐng)域,很多開發(fā)者則選擇把數(shù)據(jù)庫作為業(yè)務(wù)模塊間的數(shù)據(jù)交換,帶著這個(gè)思路來做游戲,往往會(huì)帶來很嚴(yán)重的性能問題。
簡單說,理論上,由于游戲服務(wù)器往往 7 * 24 小時(shí)持續(xù)工作,且玩家具有強(qiáng)交互性,大部分游戲世界里的數(shù)據(jù)都一直存在于內(nèi)存中。當(dāng)服務(wù)器啟動(dòng)后,一旦數(shù)據(jù)加載完畢,大部分不再需要退出內(nèi)存。服務(wù)器只是 在不斷的創(chuàng)造新數(shù)據(jù)并讓這些數(shù)據(jù)在內(nèi)存中流通而已,它沒有任何需要從外部讀取數(shù)據(jù)。如果內(nèi)存無限大,且服務(wù)器永遠(yuǎn)不會(huì)當(dāng)機(jī),數(shù)據(jù)庫這個(gè)設(shè)施沒有存在的必要。
當(dāng)然這兩個(gè)前提條件都不可能成立。
對(duì)于內(nèi)存無限大這個(gè)條件,傳統(tǒng) MMORPG 游戲需要消耗的內(nèi)存是 O(n) 的,n 和總用戶數(shù)相關(guān)。雖然同時(shí)玩游戲的用戶數(shù)(活躍用戶數(shù))有限,很難持續(xù)增長;但總用戶數(shù)的確是隨時(shí)間增長的。我們只要把 n 從總用戶數(shù)變成活躍用戶數(shù)后,基本就能維持內(nèi)存的需求。
最簡單的做法是,當(dāng)一個(gè)用戶不活躍后,就把這部分?jǐn)?shù)據(jù)落地(寫入磁盤),當(dāng)他有一天又變得活躍后,再從磁盤加載回來。在端游早期,用戶活躍的標(biāo)準(zhǔn)就 是他是否在線。我們在用戶上線的時(shí)候加載他的數(shù)據(jù),離線的時(shí)候?qū)?shù)據(jù)落地即可。從開發(fā)角度看,數(shù)據(jù)如何保存,最簡單的方法不是使用數(shù)據(jù)庫,而是以用戶 id 為文件名,把用戶數(shù)據(jù)序列化成文本寫入文件系統(tǒng)即可。這也就是網(wǎng)易早期游戲的通用做法。
對(duì)于服務(wù)器穩(wěn)定性的要求,我們不可能作到 100% 不當(dāng)機(jī),所以數(shù)據(jù)還是要定期存盤的。可以是按時(shí)間為周期保存,也可以是在關(guān)鍵操作發(fā)生時(shí)保存。這樣在災(zāi)難發(fā)生的時(shí)候可以恢復(fù)回來。
btw, 一個(gè)系統(tǒng)所需要管理的數(shù)據(jù)總量小于系統(tǒng)總的內(nèi)存量這一點(diǎn),不僅僅在游戲領(lǐng)域,其實(shí)很多別的系統(tǒng)也存在。所以 redis 這種純內(nèi)存數(shù)據(jù)庫才有了廣泛的應(yīng)用空間。redis 的 BGSAVE 以及 BGSAVE 的兩種模式,也對(duì)應(yīng)了上面所指的數(shù)據(jù)落地策略。
至于,如何操作這些數(shù)據(jù)的問題,既然數(shù)據(jù)都在你系統(tǒng)的內(nèi)存中,總可以寫出對(duì)應(yīng)的算法去處理它們吧?明白了這一點(diǎn),就能明白為什么在大多數(shù)在線游戲系統(tǒng)中,選用怎樣的數(shù)據(jù)庫就不是什么重要的問題了。
當(dāng)然,一個(gè)在線游戲的運(yùn)營還是需要大量的游戲內(nèi)數(shù)據(jù)分析的。本著不同的業(yè)務(wù)邏輯盡量分離的原則,我們還是需要把游戲內(nèi)的數(shù)據(jù)輸出出來,交給專業(yè)的系統(tǒng),專業(yè)的人來處理。這一部分的數(shù)據(jù)量遠(yuǎn)大于游戲系統(tǒng)為玩家服務(wù)時(shí)所需要的量。我認(rèn)為它的空間復(fù)雜度是 O(n * m) 的。其中有兩個(gè)維度,一是玩家的總數(shù),二是運(yùn)營的時(shí)間。游戲服務(wù)器需要把運(yùn)營過程中的數(shù)據(jù)吐出,保存到可以處理這么大數(shù)據(jù)量的數(shù)據(jù)庫中去。我們把這部分?jǐn)?shù) 據(jù)稱為運(yùn)營 log ,這個(gè)名稱我覺得不太合適,因?yàn)樗菀缀统绦蜉敵龅墓┱{(diào)試分析的錯(cuò)誤 log 相混淆,不過歷史上在網(wǎng)易工作時(shí)大家都這么叫,我也不打算起個(gè)新名詞了。
陌陌爭霸在服務(wù)器方面的選型和構(gòu)架按著這個(gè)思路做出來:
我們用 redis 保存玩家的數(shù)據(jù),考慮到玩家數(shù)量可能很多,一個(gè) redis 倉庫可能不夠,我們使用了 32 個(gè) redis 倉庫,按玩家 id 分開存放。在部署方面,可以在用戶數(shù)量較少的時(shí)候,把多個(gè) redis 倉庫部署在同一臺(tái)物理機(jī)上,再隨著用戶規(guī)模擴(kuò)大而分開部署。如果 32 個(gè)倉庫不夠的話,進(jìn)一步細(xì)分也不會(huì)是難事。
在前三個(gè)月,我們不用太考慮冷熱數(shù)據(jù)的問題,這個(gè)期間還談不上流失玩家,所有玩家數(shù)據(jù)都是熱數(shù)據(jù)。由于開發(fā)時(shí)間緊迫,我們把冷數(shù)據(jù)處理留到后期再處理。
至于數(shù)據(jù)落地的問題,redis 已有 bgsave 的能力,我們只需要細(xì)調(diào)就好了。
而運(yùn)營 log 和一些隨時(shí)間自然增長的數(shù)據(jù),比如戰(zhàn)斗錄像,我們選擇了不受內(nèi)存限制,且易于做數(shù)據(jù)分析的 mongodb 。由于擔(dān)心數(shù)據(jù)量過大,使用了 mongos 分片。
初期的設(shè)計(jì)就是這樣了,只到今天,也沒有在結(jié)構(gòu)上做什么調(diào)整。但是在操作過程中踩了許多坑,都是值得好好記錄下來的經(jīng)驗(yàn)。
預(yù)告:陌陌游戲平臺(tái)的第二款游戲:陌陌勁舞團(tuán)先于陌陌爭霸半個(gè)月上線。上線后不到兩天就宣布停服,停服時(shí)間一再延長,一直拖了一周。傳言說問題就出在數(shù)據(jù)庫這塊。下篇打算八卦一下這個(gè)事情。
原文地址。51CTO經(jīng)作者授權(quán)轉(zhuǎn)載。