好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

PHP+Redis有序集合实现24小时排行榜实时更新

基本介绍

2、当玩家的经验值发生变化时,修改该玩家的 score 值

3、使用 redis 的 ZREVRANGE 方法获取排行榜

返回有序集 key 中,指定区间内的成员。其中成员的位置按 score 值递减 (从大到小) 来排列。具有相同 score 值的成员按字典序的反序排列。 除了成员按 score 值递减的次序排列这一点外,ZREVRANGE 命令的其他方面和 ZRANGE 命令一样。

redis 127.0.0.1:6379> ZADD KEY_NAME SCORE1 VALUE1.. SCOREN VALUEN 

1、数据准备

2、获取 score 高分 top10 排名 (ZREVRANGE 为降序,ZRANGE 为升序)

3、查看用户 ee 的实际排名 (ZREVRANK 为降序,ZRANK 为升序)、实时分数

进一步需求

需要实现最近的 24 小时用户积分排行榜,并统计前 10 名的玩家和积分

实现

主要的实现思路是:

利用 ZADD 按小时划分添加用户的积分信息,然后用 ZUNIONSTORE 并集实现 24 小时的游戏积分总和,实现 “24 小时排行榜”;(如果有更好的思路,能够在下方留言不吝赐教一下就更好了)

    ZUNIONSTORE destination numkeys key [key ...] 

Redis Zunionstore 命令计算给定的一个或多个有序集的并集,其中给定 key 的数量必须以 numkeys 参数指定,并将该并集(结果集)储存到 destination 。

默认情况下,结果集中某个成员的分数值是所有给定集下该成员分数值之和 。

可能碰到的问题

1、相同分数问题

Redis 在遇到分数相同时是按照集合成员自身的字典顺序来排序,这里即是按照”user2″和”user3″这两个字符串进行排序,以逆序排序的话 user3 自然排到了前面。要解决这个问题,我们可以考虑在分数中加入时间戳,计算公式为:

带时间戳的分数 = 实际分数*10000000000 + (9999999999 – timestamp)

timestamp 我们采用系统提供的 time () 函数,也就是 1970 年 1 月 1 日以来的秒数,我们采用 32 位的时间戳(这能坚持到 2038 年),由于 32 位时间戳是 10 位十进制整数(最大值 4294967295),所以我们让时间戳占据低 10 位(十进制整数),实际分数则扩大 10^10 倍,然后把两部分相加的结果作为 zset 的分数。考虑到要按时间倒序排列,所以时间戳这部分需要颠倒一下,这便是用 9999999999 减去时间戳的原因。当我们要读取玩家实际分数时,只需去掉后 10 位即可。

初步看起来这个方案还不错,但这里面有两个问题。

第一个问题是小问题,采用秒为时间戳可能区分度还不够,如果同一秒出现两个分数相同的仍然会出现前面的问题,当然我们可以选择精度更高的时间戳,但在实际场景中,同一秒谁排前面已经无关紧要。

第二个问题是大问题,因为 Redis 的分数类型采用的是 double,64 位双精度浮点数只有 52 位有效数字,它能精确表达的整数范围为 - 2^53 到 2^53,最高只能表示 16 位十进制整数(最大值为 9007199254740992,其实连 16 位也不能完整表示)。这就是说,如果前面时间戳占了 10 位的话,分数就只剩下 6 位了,这对于某些排行榜分数来说是不够用的。我们可以考虑缩减时间戳位数,比如从 2015 年 1 月 1 日开始计时,但这仍然增加不了几位。或者减少区分度,以分钟、小时来作为时间戳单位。

如果 Redis 的分数类型为 int64,我们就没有上面的烦恼。说到这里,其实 Redis 真应该再额外提供一个 int64 类型的 ZSet,但目前只能是幻想,除非自己改其源码。

更多PHP相关知识,请访问PHP中文网!

以上就是PHP+Redis 有序集合实现 24 小时排行榜实时更新的详细内容,更多请关注Gxl网其它相关文章!

查看更多关于PHP+Redis有序集合实现24小时排行榜实时更新的详细内容...

  阅读:45次