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

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

移動(dòng)開發(fā) Android
陌陌爭霸中用于排名的分?jǐn)?shù)區(qū)間不大,也就是 0 分到 5000 分。而參與排名的人數(shù)眾多,數(shù)以百萬計(jì)。對(duì)百萬用戶做插入排序,每個(gè)插入即使是 O(N) 的也不可接受??墒聦?shí)是大量玩家的分?jǐn)?shù)相同,都是并列排名的。所以我們只需要做 5000 個(gè)桶,每個(gè)桶里僅記錄這個(gè)分?jǐn)?shù)有多少個(gè)人就可以了。

為什么大部分網(wǎng)絡(luò)服務(wù)都需要一個(gè)數(shù)據(jù)庫在后臺(tái)支撐整個(gè)系統(tǒng)?

這通常是因?yàn)榇蟛糠窒到y(tǒng)的一個(gè)運(yùn)行周期都很短,對(duì)于傳統(tǒng)的網(wǎng)站服務(wù)來說,從收到一個(gè) HTTP 請(qǐng)求開始,到終端用戶收到這個(gè)請(qǐng)求的結(jié)果為止,就是一個(gè)運(yùn)行周期。

而其間可能處理的數(shù)據(jù)集是很大的,通常沒有時(shí)間(甚至沒有空間)把所有數(shù)據(jù)都加載到內(nèi) 存,處理其中涉及的一小部分,然后保存在磁盤上再退出。

當(dāng)數(shù)據(jù)量巨大時(shí),任何對(duì)數(shù)據(jù)的操作的算法和數(shù)據(jù)結(jié)構(gòu)都需要精心設(shè)計(jì),這不是隨便一個(gè)程序員就可以輕松完成的任務(wù)。尤其是數(shù)據(jù)量大到超過內(nèi)存容量時(shí), 很多算法和數(shù)據(jù)結(jié)構(gòu)對(duì)大部分非此領(lǐng)域的程序員來說都是陌生的。本著專業(yè)的事情交給專業(yè)的人來做的原則,一般系統(tǒng)都會(huì)把這部分工作交給獨(dú)立的數(shù)據(jù)庫來完成。

對(duì)數(shù)據(jù)的操作只有抽象的足夠簡單,系統(tǒng)才能健壯,這便有了 SQL 語言做一層抽象,讓數(shù)據(jù)管理的工作可以獨(dú)立出來。甚至于你想犧牲一部分的特性來提高性能,還可以選用近年來流行的各種 NOSQL 數(shù)據(jù)庫。

可在 MMO 游戲服務(wù)器領(lǐng)域,事情發(fā)生了一點(diǎn)點(diǎn)變化。

數(shù)據(jù)和業(yè)務(wù)邏輯是密切相關(guān)的,改變非常頻繁。MMO 服務(wù)器需要持續(xù)快速的響應(yīng)用戶的請(qǐng)求。我們幾乎不可能把一切數(shù)據(jù)都放在獨(dú)立的數(shù)據(jù)庫中,比如玩家在虛擬世界中的位置,以及他所影響的其他玩家的列表;玩家 戰(zhàn)斗時(shí)的各種屬性變化,還有和玩家互動(dòng)的那些 NPC 的狀態(tài)改變……

最大的矛盾是:MMO 游戲中數(shù)據(jù)集的改變不再是簡單的 SQL 可以表達(dá)的東西,不可能交給數(shù)據(jù)庫服務(wù)期內(nèi)部完成。無論什么類型的數(shù)據(jù)庫,都不是為這種應(yīng)用設(shè)計(jì)的。如果你硬要套用其它領(lǐng)域的應(yīng)用模式的話,游戲服務(wù)器只 能頻繁的把各種數(shù)據(jù)從數(shù)據(jù)庫中讀出來,按游戲邏輯做出改變,再寫回去。數(shù)據(jù)庫變成了一個(gè)很低效的數(shù)據(jù)中轉(zhuǎn)中心,無論你是否使用內(nèi)存數(shù)據(jù)庫,都改變不了這個(gè) 低效的本質(zhì)。

我聽過無數(shù)從別的領(lǐng)域轉(zhuǎn)行到游戲領(lǐng)域做開發(fā)的程序員設(shè)計(jì)出來的糟糕系統(tǒng)。他們最終僅僅把數(shù)據(jù)庫當(dāng)成一個(gè)可靠的數(shù)據(jù)儲(chǔ)存點(diǎn)和中轉(zhuǎn)點(diǎn),認(rèn)為把所謂重要的 數(shù)據(jù)寫進(jìn)數(shù)據(jù)庫就萬事大吉,然后再別扭的從另一個(gè)位置把數(shù)據(jù)從數(shù)據(jù)庫讀出來使用。系統(tǒng)中充滿了對(duì)數(shù)據(jù)庫的奇怪異步回調(diào)用來改善系統(tǒng)的反應(yīng)速度,而系統(tǒng)卻始 終步履闌珊。能做對(duì)已經(jīng)是極限了,更何況游戲系統(tǒng)不僅僅是輸入輸出正確就是正確,如果超過了應(yīng)用的響應(yīng)時(shí)間,一切都是不正確的。

為了讓系統(tǒng)健壯,構(gòu)架師在構(gòu)架系統(tǒng)時(shí),一定會(huì)把系統(tǒng)隔離成不同的模塊,并盡量簡化模塊間的溝通規(guī)則。這樣你可以單獨(dú)校驗(yàn)每個(gè)模塊的質(zhì)量,必要的時(shí)候可以更換。幾乎沒有人會(huì)因?yàn)樾驶蜷_發(fā)方便等原因而把應(yīng)用代碼寫到 OS 內(nèi)核中去跑就是這個(gè)道理。

每個(gè)模塊只對(duì)輸入它的數(shù)據(jù)負(fù)責(zé),保證輸出的正確。通常測(cè)試也只對(duì)這個(gè)正確性負(fù)責(zé)。同學(xué)們最容易忽略的一點(diǎn)是,每個(gè)模塊都對(duì)它輸入數(shù)據(jù)的處理速度有一個(gè)上限,也就是它的吞吐量。

一旦輸入速度大于處理速度,模塊實(shí)現(xiàn)的再正確也是白搭。因?yàn)橛肋h(yuǎn)都不會(huì)有輸出了。

對(duì)于大部分模塊,只要內(nèi)存管夠,這都不是問題。實(shí)際運(yùn)作的系統(tǒng)中很少有持續(xù)大數(shù)據(jù)量的輸入的,從一個(gè)較長的時(shí)間看,總的數(shù)據(jù)輸入是小于處理能力的,暫時(shí)沒能處理的數(shù)據(jù)堆積在內(nèi)存就行了。

凡事都有例外。一個(gè)健壯的系統(tǒng)都需要對(duì)例外做處理。一個(gè)工作在 server 模式的數(shù)據(jù)庫是這樣解決這個(gè)例外的:它會(huì)支持查詢連接的并發(fā),并發(fā)的查詢相互間對(duì)計(jì)算資源的占用是公平的,相互不影響(至少是設(shè)計(jì)上的理想)。而操作系統(tǒng) 或數(shù)據(jù)庫本身會(huì)限制并發(fā)的連接數(shù),一旦達(dá)到最高連接數(shù),系統(tǒng)會(huì)拒絕服務(wù);這樣就把超過處理能力的輸入擋在了模塊外面。按這種設(shè)計(jì),就不會(huì)有輸入(只要能抵 達(dá))永遠(yuǎn)沒有回應(yīng)了。

可惜,這樣做的代價(jià)是,你必須在模塊間加入請(qǐng)求失敗的處理。一個(gè)設(shè)計(jì)不謹(jǐn)慎的系統(tǒng)最容易在錯(cuò)誤處理上栽跟頭。他們總是期望任何一個(gè)模塊都能正確處理上級(jí)的請(qǐng)求。

btw, 為什么 12306 的訂票系統(tǒng)在高負(fù)載的情況下完全不可用?就是這點(diǎn)沒處理好。我指的是,一個(gè)實(shí)現(xiàn)正確的系統(tǒng),一定不會(huì)連網(wǎng)頁的刷不出來,不給用戶正確的提示,哪怕只是錯(cuò)誤 提示;也不應(yīng)該在高負(fù)載下,有效處理能力急劇下降。我指的是,一旦用戶能進(jìn)入正常流程,就應(yīng)該順利把至少一個(gè)環(huán)節(jié)順利完成,而不是突然就卡在那里沒有任何 回應(yīng)。

快跑題了。我談到這點(diǎn),其實(shí)是想表達(dá),說的容易,做起來是很難的。下一篇我會(huì)寫到我們?cè)谶^年前出的一個(gè)事故和這個(gè)就有一些關(guān)系。

八卦時(shí)間:

陌陌勁舞團(tuán)是陌陌游戲平臺(tái)上線的第 2 款游戲。我們的陌陌爭霸還在開發(fā)的時(shí)候,這款游戲就打算上線了。我對(duì)這個(gè)產(chǎn)品有限的了解都是道聽途說,所以如果有更清楚內(nèi)情的同學(xué)發(fā)現(xiàn)說的不對(duì),也請(qǐng)諒解。對(duì)于技術(shù)問題,我想八卦的真相就并不那么重要了,有則改之,無則嘉勉。

勁舞團(tuán)這個(gè)品牌原本是屬于韓國人的,但這款游戲在國內(nèi)曾經(jīng)異?;鸨?,在國內(nèi)代理它的久游也就買下來 IP ,自己制作手機(jī)版。據(jù)我所知,陌陌勁舞團(tuán)完全是在上海開發(fā)的,沒韓國人什么事。

這是個(gè)比較簡單的游戲,至少服務(wù)器部分很簡單,也就是統(tǒng)計(jì)下分?jǐn)?shù),查查排名,以及解決一下收費(fèi)問題而已。刨掉這些部分,它就是個(gè)單機(jī)游戲,根本不需要服務(wù)器。

因?yàn)閯盼鑸F(tuán)的品牌名氣,以及陌陌巨大的用戶群,游戲一上線就在 ios 免費(fèi)榜飚上去了。如果不是企鵝公司看不順眼,立刻上線了節(jié)奏大師,估計(jì)還會(huì)在榜單上更火一些。事后證明節(jié)奏大師的上線也很倉促,完全是為了打擊競爭對(duì)手搶 著上的,因?yàn)楹笳叩姆?wù)器也不穩(wěn)定,很快就掛掉了,完全不像一個(gè)大公司應(yīng)有的質(zhì)量。

陌陌勁舞團(tuán)順利拉來了用戶后,第一天服務(wù)器就出了狀況,重啟了幾次后完全不解決問題。所以決定停服休整。一停就是三天。當(dāng)時(shí)我就納悶了,哪有修個(gè)小 bug 預(yù)計(jì)要三天的?這肯定是有結(jié)構(gòu)性問題了。當(dāng)時(shí)我們的項(xiàng)目按計(jì)劃也就最后半個(gè)月的時(shí)間了,本來陌陌的人督的我們很緊的,一下子人全飛去了上海。

一周后陌陌勁舞團(tuán)才重新上線,遠(yuǎn)超過當(dāng)初預(yù)計(jì)的 3 天。事情一解決,陌陌的技術(shù)班底,從 CTO 到下面大多數(shù)人,全部飛到廣州和我們開會(huì),讓我們重視服務(wù)器穩(wěn)定性問題。會(huì)議內(nèi)容主要是強(qiáng)調(diào)陌陌平臺(tái)初期導(dǎo)入用戶瞬間爆發(fā)量巨大,以及了解一下我們的設(shè)計(jì) 細(xì)節(jié)確保沒有大的問題。我所了解到的八卦就是在這段時(shí)間聽來的。

陌陌勁舞團(tuán)使用的是 MongoDB 。似乎這玩意很受游戲開發(fā)者喜愛。我想主要是因?yàn)橛闷饋砗唵沃苯影?。游戲從業(yè)者如果之前沒有別的領(lǐng)域的開發(fā)經(jīng)驗(yàn),對(duì)數(shù)據(jù)庫這東西一知半解的人居多。尤其是 從客戶端開發(fā)過來的人,他們通常的習(xí)慣就是看 API 文檔,了解怎么用看起來正確就夠了。然后上線測(cè)試一下,好像也對(duì),工作似乎就結(jié)束了。就算有壓力測(cè)試,也很難做到和生產(chǎn)環(huán)境一致。

上線前,據(jù)說雙方溝通過。陌陌方想確認(rèn)系統(tǒng)能不能橫向擴(kuò)展,得到的答復(fù)是可以:加硬件即可。我想陌陌勁舞團(tuán)開發(fā)方的思路是這樣的:我們的服務(wù)器系統(tǒng) 很簡單,不都是過一下數(shù)據(jù)庫么?MongoDB 是被很多人驗(yàn)證過的,不會(huì)在這么簡單的業(yè)務(wù)中出問題吧。至于負(fù)載,不是還有 mongos 么?放心啦,沒事的。

最終的直接問題出在排行榜上。當(dāng)有兩萬人在線時(shí)(沒錯(cuò),才兩萬人而已),大量用戶的排行榜查詢阻塞了數(shù)據(jù)庫。導(dǎo)致不僅僅是排行榜刷不出來,連沖值業(yè)務(wù)也受到了影響。土豪們充不進(jìn)去錢,談什么玩游戲啊。最終產(chǎn)生了雪崩,整個(gè)數(shù)據(jù)庫都不正常使得游戲系統(tǒng)工作不起來。

為啥用了這么長時(shí)間才修好這個(gè) bug ?

負(fù)責(zé)陌陌勁舞團(tuán)的服務(wù)器開發(fā)的人在項(xiàng)目做完就離職了。想想一個(gè)設(shè)計(jì)有問題的系統(tǒng)交給非設(shè)計(jì)者維護(hù)有多糟糕吧?任何清醒的程序員都知道,這個(gè)時(shí)候即使是重寫也比改問題簡單。陌陌的同學(xué)做了個(gè)正確的決定,直接派自己的人駐留在上海,把服務(wù)器重新寫了一遍。

陌陌的技術(shù)背景是 Redis 的,他們的系統(tǒng)用 redis 構(gòu)建,所以重寫就用了 redis 取代 mongodb 。寫到這里,我完全沒有 redis 和 mongodb 誰好誰差的意思。關(guān)鍵在人,你對(duì)什么熟悉就用什么,哪種數(shù)據(jù)庫都能對(duì)付這點(diǎn)小業(yè)務(wù),關(guān)鍵看你能不能用對(duì)。

Redis 里正好有一個(gè)有序集(Sorted Set) 的數(shù)據(jù)結(jié)構(gòu),你用 ZADD 插入完數(shù)據(jù)后,它就天然有序了。這個(gè)插入是 O( M * log(N)) 的時(shí)間復(fù)雜度,基本可以滿足需求。而用 ZRANGE 查詢榜單僅需 O(log(N)+M) 的時(shí)間復(fù)雜度。

那么使用 Redis ,利用 sorted set 做排行榜系統(tǒng)是我們的唯一選擇么?絕對(duì)不是。我們也不可能為了這個(gè)特性必須選擇 redis 做數(shù)據(jù)庫。但這個(gè)例子可以說明:如果數(shù)據(jù)庫提供內(nèi)在的特性可以對(duì)數(shù)據(jù)集做一些操作,我們就直接用,但需要了解這種操作的性能。它需要和整個(gè)系統(tǒng)對(duì)它的性能 期望匹配。

陌陌勁舞團(tuán)使用 mongodb 內(nèi)置的排序功能去做排行榜本也不是大問題?;蛟S僅僅只是實(shí)現(xiàn)的人對(duì) mongo 不熟悉造成的性能低下。這些隨著系統(tǒng)重建已經(jīng)無法深究了。但核心問題是,僅僅一個(gè)排行榜系統(tǒng)的錯(cuò)誤實(shí)現(xiàn)為何會(huì)影響整個(gè)系統(tǒng)的穩(wěn)定性?

下面就是我的猜測(cè)了:

許多程序員為了提高數(shù)據(jù)庫的吞吐量,并不是一個(gè)事務(wù)就給數(shù)據(jù)庫建一個(gè)連接,用完就關(guān)掉的。因?yàn)樾陆?TCP 連接是個(gè)開銷較大的操作。維持太多連接對(duì)系統(tǒng)也是一個(gè)開銷。同學(xué)們喜歡做一個(gè)叫做連接池的東西,在系統(tǒng)其它部分和數(shù)據(jù)庫對(duì)接的地方走這個(gè)連接池。只要一個(gè) 舊有連接沒有斷開,就一直把對(duì)數(shù)據(jù)庫的請(qǐng)求通過固定連接發(fā)給數(shù)據(jù)庫,等待返回。

在數(shù)據(jù)庫的吞吐量滿足系統(tǒng)需求的時(shí)候,這個(gè)模塊很容易實(shí)現(xiàn)正確。但一旦超出需求,連接池上的數(shù)據(jù)就會(huì)越積越多,數(shù)據(jù)庫查詢?cè)絹碓铰?。而調(diào)用數(shù)據(jù)庫的模塊卻不覺得這是問題。

正確的行為應(yīng)該是讓連接池快速反饋,斷開并扔掉不可能處理完的請(qǐng)求,讓請(qǐng)求方把這個(gè)不能處理的錯(cuò)誤反饋到上個(gè)環(huán)節(jié),直到流量被限制在合理的范圍內(nèi)。整個(gè)系統(tǒng)才能不至于崩潰。當(dāng)錯(cuò)誤被迫反饋到玩家那里時(shí),他頂多看到的是查詢失敗,而不太會(huì)影響到別的功能。

陌陌爭霸怎樣做排行榜的?

在上一篇里就有同學(xué)問道,如果你們不用數(shù)據(jù)庫,怎么做排行榜呢? 其實(shí)我在上一篇正文里就有解答:

“服務(wù)器只是在不斷的創(chuàng)造新數(shù)據(jù)并讓這些數(shù)據(jù)在內(nèi)存中流通而已,它沒有任何需要從外部讀取數(shù)據(jù)。如果內(nèi)存無限大,且服務(wù)器永遠(yuǎn)不會(huì)當(dāng)機(jī),數(shù)據(jù)庫這個(gè)設(shè)施沒有存在的必要。”

排行榜單也是數(shù)據(jù)之一,游戲服務(wù)器開服一刻起,沒有任何玩家有排名信息。隨著玩家名次更替,榜單才逐步形成。我們只需要在玩家分?jǐn)?shù)變化的時(shí)候同步榜單的變化即可。而玩家查詢僅僅是取走有序的榜單而已。

你看,這個(gè)過程和數(shù)據(jù)庫無關(guān)不是?需要設(shè)計(jì)的是調(diào)整榜單的算法,和榜單的數(shù)據(jù)結(jié)構(gòu)以保證維持榜單的性能足夠強(qiáng)就好了。因?yàn)橥婕颐~更替的頻率遠(yuǎn)小于玩家網(wǎng)絡(luò)包的頻率,那么這個(gè)模塊的處理能力所需要的下限很容易滿足。我們不用考慮處理不過來的情況。

針對(duì)陌陌爭霸我們是這樣做的:

陌陌爭霸中用于排名的分?jǐn)?shù)區(qū)間不大,也就是 0 分到 5000 分。而參與排名的人數(shù)眾多,數(shù)以百萬計(jì)。對(duì)百萬用戶做插入排序,每個(gè)插入即使是 O(N) 的也不可接受??墒聦?shí)是大量玩家的分?jǐn)?shù)相同,都是并列排名的。所以我們只需要做 5000 個(gè)桶,每個(gè)桶里僅記錄這個(gè)分?jǐn)?shù)有多少個(gè)人就可以了。

當(dāng)玩家分?jǐn)?shù)變遷,把原來的桶減一,新的桶加一。這個(gè)操作就是 O(1) 的。

而排行榜的查詢僅需要把當(dāng)前分?jǐn)?shù)靠前的桶累加,就能獲知查詢者的名次。對(duì)于上百萬玩家,看到哪些人和你并列的人的名字是沒有意義的。這個(gè)查詢雖然是 O(n) 復(fù)雜度,但 n 只有區(qū)區(qū) 5000 ,還可以做 cache 以應(yīng)對(duì)查詢頻率遠(yuǎn)高于更新頻率的情況。

真正需要精確知道人名的是榜單的前 200 個(gè)人,而對(duì)前 200 個(gè)人做插入排序也很快,所以并不會(huì)造成性能問題。

我們?cè)谙到y(tǒng)的單點(diǎn)做排行榜的維持,完全沒有外部數(shù)據(jù)庫操作,它只是一小段操作普通內(nèi)存結(jié)構(gòu)的 c 代碼。而這個(gè)單點(diǎn)遠(yuǎn)遠(yuǎn)成為不了整個(gè)系統(tǒng)的熱點(diǎn)。

我們?cè)谙到y(tǒng)臨時(shí)退出時(shí),把已經(jīng)排好的榜單落地,下次啟動(dòng)的時(shí)候恢復(fù)。但也不必完全信任落地的數(shù)據(jù),可以用離線腳本檢索整個(gè)數(shù)據(jù)庫重新生成一份正確的榜單。所以數(shù)據(jù)庫中的榜單只是被 cache 起來而已,系統(tǒng)運(yùn)行期間是不需要寫入數(shù)據(jù)庫的,也不用擔(dān)心數(shù)據(jù)丟失。

好吧,還是沒談到我們自己踩的坑,就又到了吃飯時(shí)間 :( 。

明天我將寫寫陌陌爭霸在運(yùn)營期間遇到的第一起數(shù)據(jù)庫事故,它和 mongos 有關(guān)。同時(shí)也會(huì)談?wù)勎覀冊(cè)诖砜袢衅陂g幫狂刃填的一些和 mongodb 有關(guān)的坑。

原文地址。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-09 23:22:26

手游開發(fā)數(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

2022-09-02 09:06:17

數(shù)據(jù)庫Oracle

2021-12-06 16:35:33

QQ微博社交軟件

2021-07-09 14:18:15

數(shù)據(jù)庫DB-EnginesOracle

2013-08-23 09:41:19

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

2022-08-03 08:26:03

數(shù)據(jù)庫OracleMySQL

2021-04-02 12:51:03

數(shù)據(jù)庫DB-EnginesMongoDB

2021-08-05 10:46:21

數(shù)據(jù)庫SQL ServerDB-Engines

2024-04-01 08:05:27

Go開發(fā)Java

2015-11-11 15:17:16

雙十一單身陌陌
點(diǎn)贊
收藏

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