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