PHP+Redis 有序汇合实现 24 小时排名榜实时更新
Redis 有序汇合和汇合一样也是 string 类型元素的汇合,且不同意反复的成员。
不一样的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为汇合中的成员停止从小到大的排序。
有序汇合的成员是独一的,但分数 (score) 却可以反复。
汇合是通过哈希表实现的,所以增加,删除,查寻的复杂度都是 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,但当前只能是梦想,除非本人改其源码。
更多PHP相关知识,请拜访PHP中文网!
以上就是PHP+Redis 有序汇合实现 24 小时排行榜实时更新的具体内容,更多请关注百分百源码网其它相关文章!