用redis實現社交產品中計數器

用redis實現計數器

社交產品業務裏有不少統計計數的功能,好比:redis

  • 用戶: 總點贊數,關注數,粉絲數
  • 帖子: 點贊數,評論數,熱度
  • 消息: 已讀,未讀,紅點消息數
  • 話題: 閱讀數,帖子數,收藏數

統計計數的特色

  • 實時性要求高
  • 寫的頻率很高
  • 寫的性能對MySQL是一個挑戰

能夠採用redis來優化高頻率寫入的性能要求。性能

redis優化方案一

對於每個實體的計數,設計一個hash結構的counter:優化

//用戶
counter:user:{userID}
                        ->  praiseCnt: 100      //點贊數
                        ->  hostCnt: 200        //熱度
                        ->  followCnt: 332      //關注數
                        ->  fansCnt: 123        //粉絲數


//帖子
counter:topic:{topicID}
                        -> praiseCnt: 100       //點贊數
                        -> commentCnt: 322      //評論數


//話題
counter:subject:{subjectID}
                            -> favoCnt: 312     //收藏數
                            -> viewCnt: 321     //閱讀數
                            -> searchCnt: 212   //搜索進入次數
                            -> topicCnt: 312    //話題中帖子數

相似這種計數器,隨着產品功能的增長,也會愈來愈多,好比回覆數,踩數,轉發數什麼的。lua

redis相關的命令

//獲取指定userID的全部計數器
HGETALL counter:user:{userID}   

//獲取指定userID的指定計數器
HMGET counter:user:{userID}  praiseCnt hostCnt 

//指定userID點贊數+1
HINCRBY counter:user:{userID}   praiseCnt

缺點:設計

這樣設計,若是要批量查詢多個用戶的數據,就比較麻煩,例如一次要查指定20個userID的計數器?只能循環執行 HGETALL counter:user:{userID}。code

優勢:ip

以實體聚合數據,方便數據管理產品

redis優化方案二

方案二是用來解決方案一的缺點的,依然是採用hash,結構設計是這樣的:hash

counter:user:praiseCnt
                        ->  userID_1001: 100
                        ->  userID_1002: 200
                        ->  userID_1003: 332
                        ->  userID_1004: 123
                                .......
                        ->  userID_9999: 213



counter:user:hostCnt
                        ->  userID_1001: 10
                        ->  userID_1002: 290
                        ->  userID_1003: 322
                        ->  userID_1004: 143
                                .......
                        ->  userID_9999: 213


counter:user:followCnt
                        ->  userID_1001: 21
                        ->  userID_1002: 10
                        ->  userID_1003: 32
                        ->  userID_1004: 203
                                .......
                        ->  userID_9999: 130

獲取多個指定userID的點贊數的命令變成這樣了:pip

HMGET counter:user:praiseCnt userID_1001 userID_1002

上面命令能夠批量獲取多個用戶的點贊數,時間複雜度爲O(n),n爲指定userID的數量。

優勢:

解決了批量操做的問題

缺點:

當要獲取多個計數器,好比同時須要praiseCnt,hostCnt時,要讀屢次,不過要比第一種方案讀的次數要少。
一個hash裏的字段將會很是寵大,HMGET也許會有性能瓶頸。

用redis管道(Pipelining)來優化方案一

對於第一種方案的缺點,能夠經過redis管道來優化,一次性發送多個命令給redis執行:

$userIDArray = array(1001, 1002, 1003, 1009);

$pipe = $redis->multi(Redis::PIPELINE);
foreach ($userIDArray as $userID) {
    $pipe->hGetAll('counter:user:' . $userID);
}

$replies = $pipe->exec();
print_r($replies);

還有一種方式是在redis上執行lua腳本,前提是你必需要學會寫lua。

相關文章
相關標籤/搜索