使用Redis实现实时排名

Redis用途很广泛,分布式用户Session缓存、爬虫URL队列、活动页面的动态列表信息等。使用Redis实现排行榜系统也是很常见的方案。

假如设计一个积分排名系统。如果积分数据都存放在数据库中,积分的更新是动态的,每次访问排行页面都需要对数据进行重新排序,在真实的产品应用中几乎是不可接受的。

Redis 提供了 sorted set 有序集合数据结构,高效的插入和删除性能,适用于需实时排序的场景。我们先抛开sorted set 的实现机制,先来尝试一下利用redis实现一个实时的排行榜系统。

zadd——新增玩家

zadd 排行榜名称 分数 玩家标识

zadd命令如果重复新增 排行榜名称和玩家标识相同,记录会被覆盖
所以我们使用zincrby实现新增用户的功能

zincrby——增减玩家分数

zincrby 排行榜名称 分数 用户
127.0.0.1:6379> ZINCRBY rank:20180601 5 user1  
"10"
127.0.0.1:6379> ZINCRBY rank:20180601 1 user2
"1"
127.0.0.1:6379> ZINCRBY rank:20180601 10 user3
"10"
127.0.0.1:6379> ZINCRBY rank:20180602 15 user1
"15"
127.0.0.1:6379> ZINCRBY rank:20180602 10 user3
"10"
127.0.0.1:6379> ZINCRBY rank:20180603 21 user2
"21"
127.0.0.1:6379> ZINCRBY rank:20180603 5 user2
"26"

ZINCRBY rank:20180603 5 user2(用户user2在20180603新增5分)

zscore——查看玩家分数

zscore 排行榜名称 玩家标识
127.0.0.1:6379> zscore rank:20180603 user2
"26"

查看user2这个玩家在20180603积累的分数。

zrevrange——查看排行榜

zrevrange 排行榜名称 起始位置 结束位置 [withscores]

由于排行榜一般是按照分数由高到低排序的,所以我们使用zrevrange,而命令zrange是按照分数由低到高排序。
起始位置和结束位置都是以0开始的索引,且都包含在内。如果结束位置为-1则查看范围为整个排行榜。
带上withscores则会返回玩家分数。

127.0.0.1:6379> zrevrange rank:20180601 0 -1 withscores
1) "user3"
2) "10"
3) "user1"
4) "10"
5) "user2"
6) "1"

查看20180601这一天所有玩家分数。

zrevrange——查看指定时间范围排行榜

ZUNIONSTORE destination numkeys key [key ...]
127.0.0.1:6379> ZUNIONSTORE rank:last_three_days 3 rank:20180601 rank:20180602 rank:20180603 WEIGHTS 1 1 1 
(integer) 3

这样就将最近3天的积分记录合并到有序集合 rank:last_three_days 中了。
权重因子 WEIGHTS 如果不给,默认就是 1。为了不隐藏细节,特意写出。
那么查询最近3天的积分榜 Top10 的信息就是:

127.0.0.1:6379> ZREVRANGE rank:last_three_days  0 9 withscores 
1) "user2"
2) "27"
3) "user1"
4) "25"
5) "user3"
6) "20"

zrevrank——查看玩家的排名

zrevrank 排行榜名称 玩家标识
127.0.0.1:6379> zrevrank rank:20180601 user2
(integer) 2

查询用户user2在20180601这一天的排名

zrem——移除某个玩家

zrem 排行榜名称 玩家标识

del——删除排行榜

del 排行榜名称

总结

上面展示了利用Redis实现实时的排行榜的排行系统。当然,要完成真正的排行榜系统还有很多细节的地方要处理,如相同分数的如何根据其他条件排序等。

同样,也可以实现对于论坛帖子的浏览统计和排序,热度的排名(可以使用ZUNIONSTORE 的 WEIGHTS 分配不同的权重 实现)。

其他命令

上面只是列出了一部分命令的使用,下面给出命令列表,可以自行组合尝试

ZADD key score1 member1 [score2 member2]
向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
ZCARD key
获取有序集合的成员数 |
ZCOUNT key min max
计算在有序集合中指定区间分数的成员数 |
ZINCRBY key increment member
有序集合中对指定成员的分数加上增量 increment |
ZINTERSTORE destination numkeys key [key …]
计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
ZLEXCOUNT key min max
在有序集合中计算指定字典区间内成员数量
ZRANGE key start stop [WITHSCORES]
通过索引区间返回有序集合成指定区间内的成员
ZRANGEBYLEX key min max [LIMIT offset count]
通过字典区间返回有序集合的成员
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT]
通过分数返回有序集合指定区间内的成员
ZRANK key member
返回有序集合中指定成员的索引
ZREM key member [member …]
移除有序集合中的一个或多个成员
ZREMRANGEBYLEX key min max
移除有序集合中给定的字典区间的所有成员
ZREMRANGEBYRANK key start stop
移除有序集合中给定的排名区间的所有成员
ZREMRANGEBYSCORE key min max
移除有序集合中给定的分数区间的所有成员
ZREVRANGE key start stop [WITHSCORES]
返回有序集中指定区间内的成员,通过索引,分数从高到底
ZREVRANGEBYSCORE key max min [WITHSCORES]
返回有序集中指定分数区间内的成员,分数从高到低排序
ZREVRANK key member
返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
ZSCORE key member
返回有序集中,成员的分数值
ZUNIONSTORE destination numkeys key [key …]
计算给定的一个或多个有序集的并集,并存储在新的 key 中
ZSCAN key cursor [MATCH pattern] [COUNT count]
迭代有序集合中的元素(包括元素成员和元素分值)

点赞