使用 Redis 進行閱讀數統計並定時持久化


之前,統計每篇博文的閱讀數的方式是經過篩選去重之后直接更新數據庫,並發壓力直接傳導到數據庫,假設1秒有1000個並發請求,傳統方案會在1秒內並發進行1000次數據庫更新操作。

為了降低數據庫的並發壓力,需要重新設計統計服務。思路是即使1秒有1萬個並發請求,也只是依次更新數據庫,對數據庫沒有並發壓力。

統計服務要做的事情很專一:去重+計數

去重的業務根據具體的需要來設計規則,例如一個用戶1個小時內所有訪問都只計算一次,沒有用戶信息的按 IP 地址或者瀏覽器標識去統計。去重就是把這些標志去重,有多種實現方法,Hash過濾,數據庫唯一性等。

這里我們采用 Redis 的 HyperLogLog ,簡稱HLL,它是一個高效的結構,內存占用極小,能快速統計出所有不一樣的元素。有三個方法:
PFADD:向結構中增加一個元素,其實 HLL 並沒有存儲這個元素,而是按照概率論的算法進行統計,所以 12K 內存就能統計 2^64 個數據,返回值為1表示該元素被統計了,反之則沒有;
PFMERGE:可以把合並兩個 HLL
PFCOUNT:獲取統計數,這個算法雖然高效,但是也有弊端,就是存在誤差,在 1% 以下,只要不是非常精確的業務基本上也是可以忽略的。

我們的業務邏輯實現比較簡單,可以用博文和時間作Keyhll_{postId}_{yyyymmddhh},再把訪問博文的用戶標志按照規則生成一個字符串,name_{userName} ip_{ipAddress},用PFADD它添加進去,HLL會判斷是否重復,重復的就不會統計,然后把不重復的也就是返回值為 1 的 Key 存儲到集合 SET 中,記錄下來方便遍歷。

經過去重之后我們就要統計總數並持久化到數據庫中,每篇博文在 Redis 中對應至少一個 HLL 結構,創建觀察者服務不停地Pop SET中所有的 hll 的 Key,然后再通過PFCOUNT 得到對應的博文的統計數。 拿到統計數之后再發送給持久化服務處理,或者通過負載均衡交給多個持久化服務處理。

如上圖所示,部署多個 Counter web服務負責接收請求,一個 redis 服務或者集群負責統計閱讀數,多個 watcher 服務負責把統計結果取出來,交給多個數據存儲服務去持久化。

我們線上用的是 docker-swarm 集群,它本身就有負載均衡作用,所以可以省略負載均衡。

這里之所以用SET.POP(),是因為它支持並發訪問的,不會鎖 Redis。如果直接遍歷所有 HLL Key,就只能用 SCAN 全局查找,雖然也不會鎖住 Redis,但是它不支持並行操作,對擴展不夠友好。

這樣架構的優點就是可以橫向擴展,任何地方出現性能瓶頸都能通過擴展解決。

參考資料
多個消費者重復消費問題
HypterLogLog
HyperLogLog 原理


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM