1、-----------------------------------------------

我们知道redis的zset是一个很好的排序工具,他会以member - score 的形式来排序,但是,当分数相同的时候,是按照member的字典序来排的,这样就有点不友好了,比如,我们要求当score相同时,先达到的排在前面,也就是最后更新时间小的排在前面。

针对这样的需求,我们必须将更新时间整合到score里面去。
我们知道score可以是整形和双精度浮点型。
要按照updatetime从小到大排序,我们可以用一个MAX-updatetime
而且,为了便于拆分真实score和辅助的数据,我们需要一个分隔符,网上有人说按照MAX-updatetime时间戳位数来分割,实际上不准,这取决于你的MAX大小。

我的做法是:

const MAX = 9999999999;// 十位

public static function updateRank($userId, $score)
{
    $key1 = "test";
    if (!Redis::exists($key1)) {
        $createScore = self::createScore(null, $score);
        Redis::zadd($key1, $createScore, $userId);
        Redis::expire($key1, 86400);
    } else {
        $source = Redis::zscore($key1, $userId);
        $createScore = self::createScore($source, $score);
        Redis::zadd($key1, $createScore, $userId);
    }

    return true;
}

public static function createScore($source, $socre){
    if(is_null($source) || $source === false){
        $score = $socre . '.' . strval( round(self::MAX - time(), 0) );
    }else{
        $arr = explode('.', $source);
        $score = intval($socre + $arr[0]) . '.' . strval( round(self::MAX - time(), 0) );
    }

    return $score;
}

最终的存储结果为:15.570245535
注意,你的真实的score尽量取整,因为小数点的精度已经很高了,不能再增加小数位的长度,但是整数位可以加到很大,基本可以满足需求。

然后取出来的时候

$temp = floatval($score);// 15.8423434494xxxxxxxx
$head = intval($temp);
$dot = ($temp - $head) * pow(10, 10);
$updatetime = round(Read3Service::MAX - $dot, 0);

MAX取值不能过大,会导致整数溢出,结果混乱。

 

2、-----------------------------------------------

1. 需求

    Redis 提供了按分数进行排序的有序集合。 比如在游戏里面,比如战斗力排行,充值排行,用默认的Redis 实现就可以达到需求。

    但是,比如等级排行,大家都是30级,谁先到30级谁第一。Redis 默认实现是,相同分数的成员按字典顺序排序(0 ~9 , A ~Z,a ~ z),所以相同分数排序就不能根据时间优先来排序。

    需要设计一个 【分数 = 等级 + 时间】 ,谁分数大谁第一,最后再根据分数能解析出来等级即可。

2.设计

    分数 = 等级 + 时间 (当前系统时间戳)

    分数是 64位的长整型 Long (有符号)

    1) 设计方式一

         long 分数,二进制用高 32位存 等级,低32位存时间(秒精度),那么数据看起是这样

         A 玩家, 10 + 1111111111(时间戳)     

         后来 B 玩家也到 10 级, 10 + 2222222222(时间戳)         

         这样排序,最终还是 B 玩家 会排到第一名,不能达到目的。

    2) 设计方式二    

         long 整数长度总共有 19位,923XXX.......,时间戳 毫秒精度 是 13位,所以只需 14 ~ 19 位存 等级,其他13位存时间。接下来看怎么存。

         等级偏移: Math.power(10, 14) = 10000000000000000(14位)

         这里有一个最大时间 MAX_TIME = 9999999999999 (13位)

         A 玩家,(10 * 等级偏移) + MAX_TIME - 11111111111111( 时间戳),最终分数 10888888888888888

         B 玩家,(10 * 等级偏移) + MAX_TIME - 22222222222222( 时间戳),最终分数 10777777777777777

         最终排序,A 玩家依然是第一。通过分数可以解析出真实 【等级 = 分数 / 等级偏移,取整】

3. 劣势

    1) 如果有三个,四个排序条件怎么办,这种情况还是推荐使用数据库,就别考虑 Redis了 。Redis 优势在于可以做到实时排行

    2) 方式二 14 ~ 19位,那么等级最大数据就只能是 919999,超过这个数就会溢出。可以把时间戳降低到秒级别,可以支持更大数字

以下例子待完善:

// 万仙阵排名变化(只受用整数分数)
public void updateWanxianzhePoints(final String avatarId, final int points) {
jedisTemplate.execute(new JedisCallback() {
  @Override
  public Object doInJedis(Jedis jedis) {
 
  long updateTime = System.currentTimeMillis();
  double timeRank = points + 1 - updateTime / Math.pow(10, (int) Math.log10(updateTime) + 1);
  jedis.zadd(FsGameDbConstants.KEY_LIST_WANXIANZHEN_POINTS, timeRank, avatarId);
  LoggerHelper.infoParams("updateWanxianzhePoints avatarId=", avatarId, "	points=", points);
  return null;
    }
  });
}
 
 
 
取出来转化成整型得到分数
 
int points = Double.valueOf(rr.getScore()).intValue();

 

1.  能够记录每个玩家的分数;                       ZADD key score member
2.  能够对玩家的分数进行更新;                   ZINCRBY key increment member 或者 ZADD key score member
3.  能够查询每个玩家的分数和名次;            分数:ZSCORE key member     排名:ZRANK key member  
4.  能够按名次查询排名前N名的玩家;         zrevrange key start stop [WITHSCORES]
5.  能够查询排在指定玩家前后M名的玩家。  通过查询指定玩家的排名 --->zrevrange key start stop [WITHSCORES]
5.  能够查询排在指定分数区域的玩家。        ZREVRANGEBYSCORE key max min [WITHSCORES]

https://www.cnblogs.com/cci8go/p/5964485.html
https://blog.csdn.net/zeus_9i/article/details/51025175?utm_source=blogxgwz0
https://blog.csdn.net/u201012980/article/details/79394614

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐