一.写在前面
- 最近做完直播的基础功能后,又多了很多相关的需求,其中有一个就是直播间分享榜单的功能,顾名思义就是:分享本直播间并成功拉用户进来的数量做一个排行。比如我分享了这个直播间,别人通过我分享的直播间链接点进来,那么这个人就是我邀请来的,我总共邀请了10个人,你总共邀请了6个人,他总共邀请了11个人。实时排名就是他>我>你。
- 简单介绍了一下功能,其实就是个根据某个权重值做排行榜的功能。
二.介绍redis的zset
这里就不说具体的zset实现了(我太菜,不敢放肆,等我牛逼了我再写zset实现,估计n年后 ),总之为了速度和稳定性以及持久化,redis肯定是最合适的,而且redis又有zSet这种数据结构,那不用zSet岂不是浪费嘛。
首先简单说一下zSet:
- Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
- 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
- 有序集合的成员是唯一的,但分数(score)却可以重复。
(这么专业的话我肯定是说不出来的,当然是网上找的啦)
命令 | 描述 |
---|---|
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] | 迭代有序集合中的元素(包括元素成员和元素分值) |
上面就是redis的zset相关的命令,项目中实际是不会这么写的,我们使用的RedisTemplate进行的redis操作
三.实现方式
介绍完zset,然后说一下功能实现思路,其实很简单。。。。。就是往zset里面塞数据
stringRedisTemplate.opsForZSet().incrementScore(key, member, incrementScore);
上面代码,其实就是reids命令的:ZINCRBY key increment member
,描述:有序集合中对指定成员的分数加上增量 increment
,比如你要给某个直播间做排行榜,key就是直播间相关的key,member就是邀请人的标识(一般就是userId),incrementScore就是这个人邀请的人数自增量(这是我的业务的说法,具体大家根据自己需求理解,融会贯通哈,比如微博热搜排序,key就可以是小时榜/天榜/月榜,member是某一条热搜词条,incrementScore就是这个词条热度增量/权重增量) ,我觉得你们肯定理解我为啥直接用incrementScore
,而不是add
,所以把自增量直接塞进去redis-zset就完事了,有人通过邀请进来了你就往进塞一次,因为这个命令是incrementScore
也就是自增类型的,所以你也不用担心刚开始的时候是否存在这个key(并发问题)
,如果调用incrementScore
的时候这个key已经存在,那就score自增,如果不存在那就新增这个key,然后把自增量设置为初始值,天然的线程安全,redis牛逼!!! 然后根据我的业务来说,我的incrementScore
自增量就是1,因为我邀请了1个人就+1,没啥特殊值,但比如你的业务是直播间但消费额排行,那你的incrementScore
自增量就是本次消费的金额数,这点应该很好理解,这里只说redis操作哈,如果这些数据还要落mysql等数据库,那看情况操作就行,比如我们的业务还要判断同一个人点了两个邀请链接,只算第一个的,并且直播结束还要拉取分享榜前三名发奖励,所以存入redis-zset的时候要校验,最后还要落库到mysql,但其实mysql很快的哈,又不是那种上千万的级别,上千万那你分库分表也不就完事了,实在不行你发一条mq消息处理呗,这种业务,我觉得保证最终一致就行了。
分享榜数据和排行问题,zset全部帮我们解决了,我们只需要给里面填入数据就行,简单、高效、实用。
剩下的就是取数据了,取数据的话也是从zset取排行的有序集合数据:
stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
上面这个代码,首先reverseRangeWithScores
方法的意思是:从大到小从第start+1名开始取值
,到第end+1名结束
,并且返回结果带分数, 简单来说就是把得分从大到小排序
,然后取前end+1名
,这个是RedisTemplate的方法,如果是redis操作的话,那命令就是上面表格中的:ZREVRANGEBYSCORE key max min [WITHSCORES]
,描述:返回有序集中指定分数区间内的成员,分数从高到低排序
,这个描述我感觉有点绕哈,但是仔细理解一下还是能理解的
举个例子哈,如果你要取排行榜前十名,那么调用上面方法的参数就是:
stringRedisTemplate.opsForZSet().reverseRangeWithScores("key", 0, 9);
这个方法就会返回排行榜前十名,这个方法返回结果是:Set
,一个Set,具体结构其实大概就是个Set,直接循环这个结果,就拿到了前十名的结果。看到这里还不了解返回结果数据结构的可以去debug一下看看这个数据结构,立马就懂哈。循环这个结果拿到的就是排好序的
,舒服啊,直接再完善一下数据,比如查一下用户名啥的,这个看具体业务哈,然后返回结果完事,简单、高效、又不担心线程安全。
好了!!!写完收工,zset实现排行榜真的很不错。本篇主要写实现思路,具体代码不能贴给你们看哈,因为是我们的业务代码,我又懒的自己写一个,所以只说思路,其实也不难,排序和并发问题redis都做好了,那还要啥自行车。