Redis 实现排行榜功能

Posted by Deadline on April 16, 2017

排行榜是个非常常见的功能,很多 app 里面都有类似的功能,自己正好最近做了这样一个功能,记录一下。

Redis 有序集合

Redis 有序集合和集合一样也是 String 类型元素的集合,且不允许重复的成员。 不同的是每个元素都会关联一个 double 类型的分数。Redis 正是通过分数来为集合中的成员进行从小到大的排序。 有序集合的成员是唯一的,但分数(score)却可以重复。 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

多字段排序

需求中有个要求是需要按照多字段排序的,比如说当分数相同的时候要比较绑定学员数量,如果绑定学员数量又相同的话就比较评价星级。Redis 只根据 socore 来排序,那么需要把这三个属性根据不同的权重来生成一个 score 值。前段时间还看到一个类似的问题,当分数相同的时候需要后加入的 member 排前面,有一种方案就是将当前时间戳作为 score 的小数部分,那么后加入的 score 就比前面加入的分值大了。总之根据实际情况来设计不同属性不同权重生成最终 score 的办法还是很好用的。

我还需要 score 是可逆的,就是将 score 取出能还原出之前的三个属性。我的办法就是将三个属性拼接在一起。比如说一个 member 的 field1 是 1,field2 是 2,field 3 是 3。 那么生成的 score 就是 10003.3 。 计算方法就是 (field1 * 10000) + field2 + (field3/10) 。但是权重具体是多少还看你实际的需求。假如 field2 有可能超过 10000 的话,那么就可能影响到 field1 的值了,你就没办法准确的逆向还原 field1 和 field2 的值了。

一些需要注意的问题

我使用的是 jedis 2.4.2 ,不同版本可能遇到的问题不一样。

ZRANK key member 返回有序集合中指定成员的索引,升序排列,就是分值越小的排前面,想要降序排列的话要使用 ZREVRANK key member 命令。凡是和降序相关的命令都是 ZREV…

返回的索引是从 0 开始的。

ZRANGEBYSCORE key min max 命令是通过分数返回有序集合指定区间内的成员,就是给出一个分数区间然后返回这个区间的 member 。经常会需要区一个值到上限或者下限的值。redis-cli 中原生命令中是用 +inf 表示正无穷,-inf 表示负无穷。如果你想知道最大最小的分数的话,你可能需要用先通过索引来取值,直接用正负无穷的话就不用了。不过这里有个坑就是在 Java 中用 jedis 的话,这个命令并不支持无穷,虽然 Double 对象中有表示无穷,但是报错了。所以得用 MAX_VALUE 来表示。

原生命令里没有指定索引返回成员的方法,就是如果我想取第一名的分数,没有直接这样的命令,但是有个 ZRANGE key start stop [WITHSCORES] 通过索引区间来返回有序集合的命令,start 和 stop 都是同一个值就可以了。ZRANGE key 0 0 就是返回第 0 名的 member,注意如果需要降序排列的话,请使用 ZREVRANGE key start stop 。


There are no comments on this post.