背景:

需求:
承接之前mongo通过存储到mongo里面的战绩表计算出用户排名并将排名信息落库到mysql,但是由于用户排名需要频繁读取且其后续在计算排名后排名不会发生变动,所以将用户排名放到了redis,采取的zset的数据格式,以用户id为value,获得的总分为score,且相同分数但是先达到此分数的人要排名在前;
问题:
redis zset分数score相同时,并不会根据插入的时间排序,而是通过其字典表根据value决定排名的先后,不满足前面说到的先达到此分数的人要排名在前。

ps:承接前文:[mongoDB] MongoTemplate实现分组聚合分页倒序条件查询

解决策略:

1、获取满足条件的用户在战绩表的取得的最大时间戳,时间戳越大则排名越后,时间戳越小则排名越小;
2、redis zset分数越大,排名越前;
3、分数相同时,引入时间戳,将时间戳转化为一个小于1且大于0的BigDecimal类型数据,满足条件,时间戳越大,得到BigDecimal数据越小,将redis分数加上此BigDecimal数据,实现相同分数,时间戳越大,分数的小数点后的值越小;时间戳越小,分数的小数点后的值越大;从而实现时间影响redis排名计算。

实现代码:

// 计算BigDecimal数据:计算规则:1.0 -(time时间戳小数点右移time时间戳长度的double数据)
private BigDecimal getScoreByTime(Long time) {
   return new BigDecimal("1.0").subtract(new BigDecimal(time*Math.pow(10, Math.negateExact(String.valueOf(time).length()))));
}

// 获取用户计入排名的最大时间戳
Long time = christmasUserActivityRanking.getCreateTime().getTime();
// 由于redis zset的同样分数的排序规则不满足当前需求(最先达到指定分数的排在前面),所以对redis的分数增加了一个小于0的浮点数
BigDecimal scoreByTime = getScoreByTime(time);
// keyRank:自定义的redis key;
// String.valueOf(christmasUserActivityRanking.getUserId()):用户userid,这里转成String是因为做了序列化
// scoreByTime.add(new BigDecimal(christmasUserActivityRanking.getTotalScore())).doubleValue()):用户取得的总分数加上根据时间戳计算出来的BigDecimal数据
redisTemplate.opsForZSet().add(keyRank, String.valueOf(christmasUserActivityRanking.getUserId()),
                    scoreByTime.add(new BigDecimal(christmasUserActivityRanking.getTotalScore())).doubleValue());
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐