首页 » 网站建设 » php及时更新技巧_PHPu0026Redis实现排行榜实时更新

php及时更新技巧_PHPu0026Redis实现排行榜实时更新

访客 2024-11-03 0

扫一扫用手机浏览

文章目录 [+]

Redis 有序凑集和凑集一样也是 string 类型元素的凑集,且不许可重复的成员。

不同的是每个元素都会关联一个 double 类型的分数。
redis 正是通过分数来为凑集中的成员进行从小到大的排序。

php及时更新技巧_PHPu0026Redis实现排行榜实时更新

有序凑集的成员是唯一的,但分数 (score) 却可以重复。

php及时更新技巧_PHPu0026Redis实现排行榜实时更新
(图片来自网络侵删)

凑集是通过哈希表实现的,以是添加,删除,查找的繁芜度都是 O (1)。
凑集中最大的成员数为 2^32 - 1^ (4294967295, 每个凑集可存储 40 多亿个成员)。

有序凑集首先是凑集,其成员(member)具有唯一性,其次,每个成员关联了一个分数(score),使得成员可以按照分数排序。

需求描述

设想在一个游戏中,有上百万的玩家数据,如果现在须要你根据玩家的履历值整理一个前 10 名的排行榜,你会怎么做呢?一样平常的做法是写一条类似下面这条 sql 语句的办法来获取:

select from game_socre order by score desc limit 0,20

这种办法在数据量较小的情形下可行,但是在数据量大的情形下查询速率将变慢,特殊是还须要联表查询时,速率低落的就更明显了。

实现

这时你可以考虑利用 redis 来实现这个功能。

实现这个功能紧张用到的 redis 数据类型是 redis 的有序凑集 zset。
zset 是 set 类型的一个扩展,比原有的类型多了一个顺序属性。
此属性在每次插入数据时会自动调度顺序值,担保 value 值按照一定顺序连续排列。

紧张的实现思路是:

1、在一个新的玩家参与到游戏中时,在 redis 中的 zset 中新增一条记录(记录内容看详细的需求)score 为 0

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,但目前只能是抱负,除非自己改其源码。

标签:

相关文章