PV統計優化設計

一 原由

公司要對以前的pv,uv統計進行重構,原先的不許,並且查詢速度很慢。前端

經調研發現這絕對是一個坑,pv、uv統計存在的設計看起來簡單,可是瞬間流量大,特別是有搶購等功能時,設計不良會致使數據庫訪問壓力大,還存在被用心不良者利用等狀況。redis

系統原先設計是將用戶的請求放到redis中去,然後天天晚上一次將數據同步到數據庫,在redis中並無保存每一個用戶的訪問時間,而只是保存的是每分鐘有多少pv、uv。
這種設計存在這樣一些問題:sql

  1. 只統計到分鐘,並不統計每一個訪問的具體時間,數據參考價值有限
  2. 對pv的查詢會從redis中查一部分,從數據庫中再查一部分,合併起來返回前端,開發實現上代碼比較複雜。更別說在數據庫中查詢竟然是用where min=xx來實現,1000分鐘時間段的查詢會查詢1000次數據庫,查詢返回奇慢無比,能寫出這個sql的簡直是天才。
  3. 統計一次pv的redis操做要操做6次,數據結構的使用上存在問題。
redisTemplate.opsForValue().increment(nowMin,1);
int pv = redisTemplate.opsForValue().get(nowMin);
redisTemplate.opsForHash().put(PV_KEY, nowMin, pv);
redisTemplate.opsForSet().put(nowMin,uid);
int uv = redisTemplate.opsForSet().size(nowMin);
redisTemplate.opsForHash().put(UV_KEY, nowMin, pv);

原代碼甚至要8次,這裏無力吐槽,徹底不把redis當資源,你知道如何能優化成一個redis操做麼?數據庫

並且進行pv,uv統計確定是要精確到用戶的,這樣才能看出什麼用戶進行了什麼訪問,方便後期的用戶畫像以及訪問數統計。但如此一來帶來的問題就是後端

  1. 數據庫記錄會急劇增加,之前只是統計分鐘,一天也就1000+條數據,而若是粒度是細到用戶的話,若是PV到千萬級,即便天天同步也受不了
  2. 實時查詢會從redis中取數據,redis中資源原本就稀缺,若是天天同步一次,意味着要從redis中取百萬、甚至千萬級數據,不只同步會很是慢,並且沒法知足實時查詢的需求。

系統自己存在以下限制瀏覽器

  1. 必須使用oracle數據庫,並且pv表與業務表就在同一個實例,要考慮不能有瞬時過大的流量影響到業務操做。
  2. 必須使用同一個微服務網關。

二 第一步

經思考實現了以下方案:緩存

  1. 使用瀏覽器指紋來記錄每個用戶,來記錄uv,而不是使用用戶id,將pv、uv統計與業務隔離。瀏覽器指紋是根據客戶端的一些參數計算而成,業內已經有成熟解決方案,準確率能達到94%
  2. 優化入統計pv、uv時入redis的操做,從新設計redis的數據格式,將6次縮減爲1次。

    redisTemplate.opsForHash().put(bizKey, devFinger + dateTime);數據結構

    只需一次redis操做, 落到數據庫後用group by查詢就能很是方便的按照分鐘,小時,天來分組了oracle

  3. 請求進來後不直接入庫,也不直接入redis,而是放入mq,經過mq再入redis,起到削峯填谷的做用。測試環境redis存的速度大概能達到 3w/s, 3w的qps,taobao搶購系統恐怕都支撐起來了。
  4. 每分鐘將redis的數據批量入數據庫,而不是天天統計一次,由於千萬級的數據統計不只對oracle數據庫,並且對redis都是巨大的壓力,甚至極可能會致使讀redis超時。同時將寫數據庫的操做分配到每分鐘,下降數據庫的壓力
  5. 查詢只從數據庫中查,加上對應的索引,查詢數據不會太慢。同時也下降了程序的複雜性,不用到redis中查了。

三 第二步

壓測發現有兩個問題微服務

  1. mq掛了
  2. 同步到數據庫時數據庫也處理不過來。

咱們mq是公用的,就是說全部的服務,並且不止咱們的服務,都用到了mq,而pv,uv操做是一個超級大數量級別的操做,並且並不是核心業務,因此不能把主要的資源都放到mq上,因此咱們又繼續進行了處理:

  1. 後端用了緩存隊列,當pv知足10個的時候才發送,不然不發。此處要加synchonize,不然會出現異常的
  2. 數據庫同步時作了柔性處理,當pv數據量過大的時候不處理,而是延後再作,等到pv量降下來後再處理
相關文章
相關標籤/搜索