自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

談?wù)勀澳盃幇栽跀?shù)據(jù)庫方面踩過的坑(mongoDB篇)

移動開發(fā) Android MongoDB
無論什么系統(tǒng),在對性能有要求的環(huán)境下,完全當(dāng)黑盒用都是不行的。游戲更是如此。上篇我就談過,我們絕對不可能把游戲里數(shù)據(jù)的變化全部扔到數(shù)據(jù)庫中去做。傳統(tǒng)數(shù)據(jù)庫并非為游戲設(shè)計(jì)的。

我們公司開始用 mongoDB 并不是因?yàn)殚_始的技術(shù)選型,而是我們代理的第一款游戲《 狂刃 》的開發(fā)商選擇了它。

這款游戲在我們代理協(xié)議簽訂后,就進(jìn)入了接近一年的共同開發(fā)期。期間發(fā)現(xiàn)了很多和數(shù)據(jù)庫相關(guān)的問題,迫使我們熟悉了 mongoDB 。在那個期間,我們搭建的運(yùn)營平臺自然也選擇了 mongoDB 作為數(shù)據(jù)庫,這樣維護(hù)人員就可以專心一種數(shù)據(jù)庫了。

經(jīng)過一些簡單的了解,我發(fā)現(xiàn)國內(nèi)很多游戲開發(fā)者都不約而同的采用了 mongoDB ,這是為什么呢?我的看法是這樣的:

游戲的需求多變,很難在一開始就把數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)清楚。而游戲領(lǐng)域的許多程序員的技術(shù)背景又和其他領(lǐng)域不同。

在設(shè)計(jì)游戲服務(wù)器前,他們更多的是在設(shè)計(jì) 游戲的客戶端:畫面、鍵盤鼠標(biāo)交互、UI 才是他們花精力最多的地方。對該怎么使用數(shù)據(jù)庫沒有太多了解。這個時候,出現(xiàn)了 mongoDB 這樣的 NOSQL 數(shù)據(jù)庫。mongoDB 是基于文檔的,不需要你設(shè)計(jì)數(shù)據(jù)表,和動態(tài)語言更容易結(jié)合??雌饋砗苊篮?,你只需要把隨便一個結(jié)構(gòu)的數(shù)據(jù)對象往數(shù)據(jù)庫里一塞,然后就祈禱數(shù)據(jù)庫系統(tǒng)會為你搞定其它的事情。如果數(shù)據(jù)庫干的不錯,性能不夠,那是數(shù)據(jù)庫的責(zé)任,和我無關(guān)??吹侥切┰u測數(shù)據(jù)又表明 mongoDB 的性能非常棒,似乎沒有什么可擔(dān)心的了。

其實(shí)無論什么系統(tǒng),在對性能有要求的環(huán)境下,完全當(dāng)黑盒用都是不行的。

游戲更是如此。上篇我就談過,我們絕對不可能把游戲里數(shù)據(jù)的變化全部扔到數(shù)據(jù)庫中去做。傳統(tǒng)數(shù)據(jù)庫并非為游戲設(shè)計(jì)的。

比如,你把一群玩家的坐標(biāo)同步到數(shù)據(jù)庫,能夠把具體某個玩家附近玩家列表查詢出來么?mongoDB 倒是提供了 geo 類型,可以用 near 或 within 指令查詢得到附近用戶。可他能滿足 10Hz 的更新頻率么?

我們可以把玩家的 buf 公式一一送入數(shù)據(jù)庫,然后修改一些屬性值,就可以查詢到通過 buf 運(yùn)算得到的結(jié)果么?

這類問題有很多,即使你能找到方法讓數(shù)據(jù)庫為你工作,那么性能也是堪憂的。當(dāng)我們能在特定的數(shù)據(jù)庫服務(wù)內(nèi)一一去解決她們,最終數(shù)據(jù)庫就是一個游戲服務(wù)器了。

狂刃這個項(xiàng)目在我們公司是負(fù)責(zé)平臺建設(shè)的蝸牛同學(xué)跟的。我從他那里聽來了許多錯誤使用 mongoDB 的趣聞。

一開始,整個數(shù)據(jù)庫完全沒有為查詢建索引。在沒什么數(shù)據(jù)的情況下,即使所有的查詢都是 O(N) 的,遍歷整個數(shù)據(jù)庫,也不會有問題??上攵脩袅恳簧蟻?,性能會下降的多快。

然后,數(shù)據(jù)庫又被建立了大量的無用的索引,和一些錯誤的復(fù)合索引,同樣惡化了系統(tǒng)。感覺就是哪里似乎有點(diǎn)性能問題,那就是少了個索引的緣故。這種病 急亂投醫(yī)的現(xiàn)象,在項(xiàng)目開發(fā)后期很容易出現(xiàn)。其實(shí)解決方法很簡單:主導(dǎo)設(shè)計(jì)的人只要靜下心來好好想一想,數(shù)據(jù)庫系統(tǒng)其實(shí)也就是一個管理數(shù)據(jù)的封閉模塊。如 果你來管理這些數(shù)據(jù),怎樣的數(shù)據(jù)結(jié)構(gòu)更利于滿足特定的檢索,需要哪些索引數(shù)據(jù)輔助。

最終的問題依舊是算法和數(shù)據(jù)結(jié)構(gòu),不同的是,不需要你實(shí)現(xiàn)它,而需要你理解它。

另外,數(shù)據(jù)庫是被設(shè)計(jì)成可以并發(fā)訪問的,而并發(fā)永遠(yuǎn)是復(fù)雜的東西。mongoDB 缺乏事務(wù)操作,需要用文檔操作的原子性來模擬。這很容易被沒經(jīng)驗(yàn)的人用錯(這是個怪圈,越是沒數(shù)據(jù)庫經(jīng)驗(yàn)的人越喜歡 mongoDB ,因?yàn)橄拗粕?,看起來更自然。)?/p>

狂刃出過這樣一個 bug :想讓用戶注冊的時候用戶名唯一,所以在用戶注冊的時候先查一下數(shù)據(jù)庫看用戶名是否存在,如果不存在就允許創(chuàng)建一個這個名字的用戶??上攵?,上線運(yùn)營不出一天,同名用戶就會出現(xiàn)了。

因?yàn)楣卷?xiàng)目需要,我給 skynet 增加了 mongo driver 。老實(shí)說,實(shí)現(xiàn)這個 driver 的時候,我對 mongo 就興趣寥寥。最后只實(shí)現(xiàn)了最底層的通訊協(xié)議,光這個部分,它的協(xié)議設(shè)計(jì)就已經(jīng)是很難看的了。但是即使這樣,我也耐著性子把這部分做完,而不想使用現(xiàn)成的 driver 。

mongo 的官方 driver 都是內(nèi)置 socket 通訊模塊的。這種做法很難單獨(dú)把協(xié)議解析部分提取出來,附加到自己項(xiàng)目的 IO 模型中去。(btw, redis 這方面就好的多,因?yàn)樗膮f(xié)議足夠簡單,你可以用幾十行代碼就實(shí)現(xiàn)它的通訊協(xié)議,而不需要依賴 driver 模塊。)

狂刃服務(wù)器的 IO 采用的 boost.asio ,我很好奇他是怎樣把 mongoDB 官方 C++ driver 整合進(jìn)去的。不出所料,他們開了一個獨(dú)立線程處理 mongo 的數(shù)據(jù),然后把數(shù)據(jù)對象跨線程發(fā)出來。細(xì)究這個實(shí)現(xiàn)就能看出問題來。程序員很容易誤解 mongoDB client api 的內(nèi)在含義。

一開始,狂刃的開發(fā)同學(xué)以為從 mongo 中取到一組查詢結(jié)果后,調(diào)用 cursor 的 findnext 只在對象內(nèi)存中迭代,所有結(jié)果都是一開始一次性返回的。以為把一開始的 bson 對象從 mongo 線程轉(zhuǎn)移到主線程中就好了。可事實(shí)并不是這樣,mongo 一次只會返回一組查詢結(jié)果,當(dāng)結(jié)果迭代完時,findnext 還會自動提交新的查詢請求。這時,對象已經(jīng)不在原有的 mongo 線程中了。

學(xué)過 C++ 的同學(xué)可以想像一下,讓你去 code review 不是你參于的 C++ 項(xiàng)目去找到 bug 需要多少功夫?對了,你還要在想像中要加上被各種 boost.asio 回調(diào)函數(shù)拆得支離破碎的業(yè)務(wù)流程。所以去年有那么一段日子,我們需要完全停下手頭其他的工作,認(rèn)真的從頭閱讀那數(shù)以萬行計(jì)的 C++ 代碼。

老八卦別人似乎不太厚道,下面來談?wù)勎覀冏约悍傅腻e誤。

陌陌爭霸出的第一起服務(wù)器事故是在 2014 年一月中旬的一個周末。準(zhǔn)確說,這次算不上重大運(yùn)營事故,因?yàn)闆]有玩家數(shù)據(jù)受損,也沒有意外停服。但卻是我們第一次發(fā)現(xiàn)早先設(shè)計(jì)中有考慮不足的地方。

1 月 12 日周日。下午 17 點(diǎn)左右,我們的 SA Aply 發(fā)現(xiàn)我們運(yùn)營用的 log 延遲了 3 個小時才到運(yùn)營平臺。但數(shù)據(jù)還是源源不斷的進(jìn)入,系統(tǒng)也很穩(wěn)定,就沒有特別深究。

到了晚上 20 點(diǎn)半,平臺組的劉陽報告說運(yùn)營數(shù)據(jù)已經(jīng)延遲了 5 個小時了,這才引起了大家的警覺。由于是周末,開發(fā)人員都回家休息了,曉靖 21 點(diǎn)上線檢查,這時發(fā)現(xiàn)游戲服務(wù)器內(nèi)存占用比平常同期高了 10G 之多,并在持續(xù)上升。

我大約是在 21 點(diǎn)接到電話的,在電話中討論分析了一下,覺得是 log 數(shù)據(jù)從 skynet 的 log 服務(wù)發(fā)走,可能被積壓在 socket server 的一個鏈表上。這段代碼并不復(fù)雜,插入新的寫入數(shù)據(jù)是 O(1) 操作,所以沒有阻塞玩家游戲的風(fēng)險。而輸出 log 的頻率還不至于短期把所有內(nèi)存吃光。游戲服務(wù)器暫時是安全的。

晚21點(diǎn)40分,雖然沒能分析出事故的源頭,但我們立刻采取了應(yīng)急方案。重新啟動了一套游戲服務(wù)器,在線將舊服務(wù)器上的 80% 玩家導(dǎo)到新的備用服務(wù)器上。并同時啟動了新的 log 數(shù)據(jù)庫集群。打算挺到周一再在固定維護(hù)時間處理。

晚 23 點(diǎn),新啟動的游戲服務(wù)器也出現(xiàn)了 log 輸出延遲。因?yàn)檫\(yùn)營 log 是輸出到一個 mongos 管理的集群中的,我們嘗試在舊的集群(已無新數(shù)據(jù)寫入,但依舊沒有消化完滯留的舊數(shù)據(jù))做了刪除部分索引的嘗試,沒有什么效果。

凌晨 0:45 ,開啟了新的備機(jī)群,取消了 mongos ,讓每臺機(jī)器獨(dú)立連接一個單獨(dú)的 mongoDB ,情況終于好轉(zhuǎn)了。

以上,是當(dāng)時事故記錄的節(jié)選。

徹底搞明白事故起源是周二的事情了。

表面上看起來是在 mongos 服務(wù)上堆積了大量的數(shù)據(jù)庫插入操作。讓這個單點(diǎn)過載了。我們起初的運(yùn)營 log 輸出是有點(diǎn)偏多,比如每個士兵的訓(xùn)練都有一條單獨(dú)的 log ,而陌陌爭霸游戲中這種 log 是巨量的。我們裁減并精簡了一部分 log 但似乎并不能從根本上解釋這起事故。

問題出在 mongos 的 shard key 的選擇上。mongo 可以指定 document 的若干字段為 shard key ,mongos 把這個 key 當(dāng)成一個整數(shù),按整數(shù)區(qū)間把 document 分成若干個桶。再把桶均勻分配到背后的從機(jī)上。

如果你的 key 是有規(guī)律的數(shù)字,而你又需要這種規(guī)律不至于破壞桶分配的公平性,你還可以將一個 hash 算法應(yīng)用于原始選擇的 key 上,讓 key 足夠散列開。我們一開始就是按自增 id 的散列結(jié)果做 key 的。

錯誤的 shard key 選擇就是這起事故的罪魁禍?zhǔn)住?/p>

因?yàn)槲覀兪谴罅康捻樞驅(qū)懖僮?,?yīng)該優(yōu)先保證寫入的流暢。如果用隨機(jī)散列的方式去看待這些 document 的話,新舊 log 就很大幾率被分配到一起。而 mongo 并不是一條一個單位將數(shù)據(jù)落地的,而是一塊塊的進(jìn)行。這種冷熱數(shù)據(jù)的交織會導(dǎo)致寫盤 IO 量遠(yuǎn)遠(yuǎn)大于 log 實(shí)際的輸出量。

最后我們調(diào)整了 shard key ,按 log 時間和自增 id 分開,就把 mongo 數(shù)據(jù)落地的 IO 量下降了幾個數(shù)量級。

看吧,理解系統(tǒng)如何工作的很重要。

ps, 這起事故后,我給 skynet 加了更多的監(jiān)控,方便預(yù)警單個模塊的過載。這幫助我們更快的定位后面出現(xiàn)的問題。那些關(guān)于 redis 的故事,且聽下回分解。

3 月 5 日補(bǔ)充:

根據(jù)下面的留言討論,總結(jié)一下:

關(guān)于 shard key 的選擇在 mongoDB 文檔中被討論過 。但和我們遇到的情況有所不同。

有同學(xué)提到,這篇文章里描述在批量寫入的時候,數(shù)字做 key 要比 hash 過的有更高的效率 。

我們沒有使用批量插入,而我們是單條逐條插入的。所以性能低下并不在于逐條調(diào)用 getLastError ,我們?yōu)榱吮WC寫入性能,都是單向推送,不獲取 getLastError 的(最低 Write concern 級別)。我認(rèn)為在我們的業(yè)務(wù)情況下,按時間片讓一臺機(jī)器接受一組數(shù)據(jù)是更好的利用方式。

原文地址。51CTO獲作者授權(quán)轉(zhuǎn)載。

責(zé)任編輯:徐川 來源: blog
相關(guān)推薦

2014-03-05 09:31:54

陌陌爭霸數(shù)據(jù)庫

2014-03-09 23:29:12

2014-03-05 13:27:09

陌陌爭霸數(shù)據(jù)庫

2015-03-31 18:26:43

陌陌社交

2012-08-31 14:36:19

陌陌林志霖社交應(yīng)用

2012-08-27 09:39:23

陌陌社交APP

2014-07-25 15:41:12

陌陌WOT2014GoRedis

2021-12-06 16:35:33

QQ微博社交軟件

2014-08-15 17:51:39

聽云

2015-05-12 14:34:09

陌陌

2015-12-11 16:07:03

光合資本

2015-05-27 11:05:46

阿里云陌陌CDN

2024-04-01 08:05:27

Go開發(fā)Java

2015-11-11 15:17:16

雙十一單身陌陌

2012-08-21 15:52:48

2015-07-24 16:49:40

陌陌禮物

2012-11-14 09:45:57

陌陌

2012-08-23 16:41:10

陌陌投資

2022-04-26 21:49:55

Spring事務(wù)數(shù)據(jù)庫

2014-05-13 14:11:36

GoRedis
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號