社交产品业务里有不少统计计数的功能,好比:python
能够采用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 //话题中帖子数
相似这种计数器,随着产品功能的增长,也会愈来愈多,好比回复数,踩数,转发数什么的。优化
//获取指定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}。lua
优势:以实体聚合数据,方便数据管理设计
方案二是用来解决方案一的缺点的,依然是采用hash,结构设计是这样的:code
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的点赞数的命令变成这样了ip
HMGET counter:user:praiseCnt userID_1001 userID_1002
上面命令能够批量获取多个用户的点赞数,时间复杂度为O(n),n为指定userID的数量。产品
优势:解决了批量操做的问题hash
缺点:当要获取多个计数器,好比同时须要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。