基于redis的zset实现排行榜功能

2017-09-27  本文已影响0人  lunwuciyu

临近中秋,公司需要开发一款微信小游戏,里面有一个排行榜的功能
主要需求包括:

  1. 用户可以上传每次游戏的分数,系统返回该用户的最高分和最高分排名(分数相同时,时间优先);
  2. 用户可以查询排行榜,返回top50,和自己所在的排名

最开始是想使用数据库来实现,保存每个用户最高分的记录,主要字段【name, score, createTime】

这样对于数据库的查询压力会比较大,而且只是一个临时活动,也没必要专门创建一张表来实现

然后和同事讨论可不可以参考HashMap的原理,使用数组+链表的形式的来实现,将分数作为数组下标,每个数组元素上是链表,按照达到该分数的先后顺序保存用户

数组+链表

最后都懂得,搜索引擎,看到redis 的zset原来是解决排行榜的标配,天生就是来做排行榜的,参考了一些网上的文章http://bbs.gameres.com/thread_461758.html

获取top50的逻辑

public List<MidAutumnView> getRangeTop(Long userId) {
        // top
        Set<String> midAutumnStrs = stringRedisTemplate.opsForZSet().range(MID_AUTUMN, 0, TOP_NUM);
        List<MidAutumnView> midAutumnViews = Lists.newArrayList();
        Iterator<String> iterator = midAutumnStrs.iterator();
        int i = 1;
        while (iterator.hasNext()) {
            midAutumnViews.add(convStr2MidAutumnView(iterator.next(), i));
            i++;
        }
        // 判断是否在末尾追加自己
        String midAutumnStr = stringRedisTemplate.opsForValue().get(MID_AUTUMN_USER + userId);
        MidAutumnView midAutumnView = JSONObject.parseObject(midAutumnStr, MidAutumnView.class);
        Long rank = stringRedisTemplate.opsForZSet().rank(MID_AUTUMN,
            convView2ItemStr(midAutumnView));
        if (rank != null && rank > TOP_NUM) {
            midAutumnViews.add(midAutumnView);
        }
        return midAutumnViews;
    }

提交新分数的逻辑

public MidAutumnView putScore(RedisRankItem redisRankItem) {
        String midAutumnStr = stringRedisTemplate.opsForValue()
            .get(MID_AUTUMN_USER + redisRankItem.getUserId());
        // 首次提交分数
        if (StringUtils.isEmpty(midAutumnStr)) {
            stringRedisTemplate.opsForZSet().add(MID_AUTUMN, JSONObject.toJSONString(redisRankItem),
                redisRankItem.buildScore(redisRankItem.getScore()));
            Long rank = stringRedisTemplate.opsForZSet().rank(MID_AUTUMN,
                JSONObject.toJSONString(redisRankItem));
            MidAutumnView midAutumnView = MidAutumnView.builder().userId(redisRankItem.getUserId())
                .name(redisRankItem.getName()).portraitUrl(redisRankItem.getPortraitUrl())
                .score(redisRankItem.getScore()).createTime(redisRankItem.getCreateTime())
                .rank(rank.intValue()).maxScore(redisRankItem.getScore()).build();
            stringRedisTemplate.opsForValue().set(MID_AUTUMN_USER + redisRankItem.getUserId(),
                JSONObject.toJSONString(midAutumnView));
            return midAutumnView;
        } else {
            // 二次提交分数
            MidAutumnView midAutumnView = JSONObject.parseObject(midAutumnStr, MidAutumnView.class);
            midAutumnView.setScore(redisRankItem.getScore());
            midAutumnView.setCreateTime(redisRankItem.getCreateTime());
            // 更新最高分
            if (redisRankItem.getScore() > midAutumnView.getScore()) {
                stringRedisTemplate.opsForZSet().remove(MID_AUTUMN,
                    convView2ItemStr(midAutumnView));
                stringRedisTemplate.opsForZSet().add(MID_AUTUMN,
                    JSONObject.toJSONString(redisRankItem),
                    redisRankItem.buildScore(redisRankItem.getScore()));
                Long rank = stringRedisTemplate.opsForZSet().rank(MID_AUTUMN,
                    JSONObject.toJSONString(redisRankItem));
                midAutumnView.setMaxScore(redisRankItem.getScore());
                midAutumnView.setRank(rank.intValue());
                stringRedisTemplate.opsForValue().set(MID_AUTUMN_USER + redisRankItem.getUserId(),
                    JSONObject.toJSONString(midAutumnView));
            }
            return midAutumnView;
        }
    }

为了排错,最后用户的每次提交都会记录到mongo

上一篇下一篇

猜你喜欢

热点阅读