程序員經(jīng)典面試題:如何實(shí)現(xiàn)一個(gè)海量計(jì)數(shù)系統(tǒng)
我們?cè)陂_(kāi)發(fā)中,經(jīng)常有一些計(jì)數(shù)相關(guān)的工作。例如,抖音上的短視頻,有評(píng)論數(shù),播放數(shù)。發(fā)送微博的時(shí)候,也有轉(zhuǎn)發(fā)數(shù),評(píng)論數(shù),點(diǎn)贊數(shù),電商網(wǎng)站上,有著商品售賣(mài)金額,商品的出售件數(shù)等統(tǒng)計(jì)。那么,這些統(tǒng)計(jì)是怎么做到的呢?
不難想到,我們可以在用戶查看這條評(píng)論的時(shí)候,去統(tǒng)計(jì)總共有多少個(gè)贊。舉個(gè)例子,你在虎撲上發(fā)表一條評(píng)論,每當(dāng)有人點(diǎn)贊的時(shí)候,都會(huì)在評(píng)論下面增加一條點(diǎn)贊記錄,記錄誰(shuí)誰(shuí)誰(shuí)點(diǎn)贊了。當(dāng)有人訪問(wèn)這條評(píng)論的時(shí)候,我們可以使用Mysql的SelectCount語(yǔ)句,對(duì)其進(jìn)行統(tǒng)計(jì),查詢總共有多少條記錄,并且返回給前段。這種方案有什么問(wèn)題呢?當(dāng)數(shù)據(jù)量小的時(shí)候,我們總可以非常迅速地計(jì)算到結(jié)果。但是隨著數(shù)據(jù)量的增大,例如一條非常火的評(píng)論,可能已經(jīng)有數(shù)十萬(wàn)個(gè)贊了,每次都去查詢效率都比較低。評(píng)論的內(nèi)容是核心數(shù)據(jù),評(píng)論的點(diǎn)贊數(shù)是次要數(shù)據(jù),如果查詢點(diǎn)贊數(shù)量的耗時(shí)過(guò)長(zhǎng),反而會(huì)影響主要業(yè)務(wù)。我們要做的,是盡量地少去查詢,或者讓查詢的速度更快。
在此之前的方案,我們每次都是全量Count一遍,那么,我們能不能把這個(gè)數(shù)據(jù)存起來(lái)呢?這樣子,就不需要每次有人來(lái)查詢的時(shí)候,都去統(tǒng)計(jì)。每次有變更的時(shí)候,就去數(shù)據(jù)庫(kù)里面進(jìn)行selectCount,查詢后將結(jié)果保存下來(lái)。之后每次有人訪問(wèn)這條評(píng)論,我們就直接返回,而不需要進(jìn)行SelectCount。當(dāng)然,每次都去SelectCount也是一個(gè)笨方法,我們?yōu)槭裁床幻看斡腥它c(diǎn)贊就+1,有人取消點(diǎn)贊就減一呢。我們將點(diǎn)贊數(shù)據(jù)的電話分為兩種事件,一種是+1,另一種是-1。當(dāng)然,這種做法的潛在風(fēng)險(xiǎn)點(diǎn)就是并發(fā)問(wèn)題,例如原來(lái)的數(shù)據(jù)是5,突然來(lái)了兩個(gè)+1的請(qǐng)求,由于他們并發(fā)執(zhí)行了,最終的結(jié)果變成6。這種情況下我們需要加鎖防止并發(fā),或者讓數(shù)據(jù)庫(kù)幫我們實(shí)現(xiàn)。
僅是這樣是遠(yuǎn)遠(yuǎn)不夠的,我們?nèi)绾蚊鎸?duì)突發(fā)的流量呢?例如突然爆發(fā)了一個(gè)熱點(diǎn)事件,突然火了一條評(píng)論,可能10分鐘之內(nèi)就已經(jīng)幾萬(wàn)個(gè)點(diǎn)贊了。假如我們每次都去數(shù)據(jù)庫(kù)更新,那勢(shì)必效率會(huì)大大降低。為了應(yīng)對(duì)這種突發(fā)性的流量,業(yè)內(nèi)的解決方案大家其實(shí)都非常熟悉,不就采用消息隊(duì)列進(jìn)行削峰嘛。但是,這種突發(fā)性的流量容易造成MQ堆積,那么有沒(méi)有什么好的方法呢?其實(shí)非常簡(jiǎn)單,本來(lái)我們要進(jìn)行10次+1的操作,為什么我們不合并成1次+10操作呢。這樣就能大大減少數(shù)據(jù)庫(kù)的壓力,我們一般有兩場(chǎng)常見(jiàn)的實(shí)現(xiàn)方式,一種是對(duì)異步隊(duì)列進(jìn)行任務(wù)合并,另外一種是我們可以將計(jì)數(shù)存在緩存上,定期將數(shù)據(jù)刷到數(shù)據(jù)庫(kù)中。
我們更多采用的是第二種方法,并且我們還會(huì)進(jìn)一步優(yōu)化,就是我們會(huì)將點(diǎn)贊的統(tǒng)計(jì)存放在Redis這種的緩存中間件上,然后再定期刷到磁盤(pán)上。對(duì)于這樣的統(tǒng)計(jì)數(shù)據(jù),冷熱是非常明顯的,基本上,采用采用這種方案,已經(jīng)可以適用大部分計(jì)數(shù)系統(tǒng)了。歡迎大家關(guān)注我,共同學(xué)習(xí),共同進(jìn)步。大家的支持是我繼續(xù)嘮嗑的動(dòng)力。






















