Redis大数据相关

redis zset做排行榜

2018-08-04  本文已影响503人  黄二的NPE
  • 引子

直播运营活动中经常会有这样的需求,根据用户送礼情况做排名。这个排行榜具有以下特点:

  1. 用户每次请求会返回用户的排名
  2. 送礼金额越多粉丝排名越靠前
  3. 相同金额送礼越早越靠前
  4. 排行榜会随着粉丝送礼变化而不断变化
  • 排行榜的实现方式
表结构
CREATE TABLE `user` (
  `id` int(10) NOT NULL COMMENT '编号',
  `uid` varchar(32) NOT NULL COMMENT '用户',
  `coin` int(10) NOT NULL COMMENT '用户送出金额',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
1. sql查询
EXPLAIN SELECT
    *
FROM
    (
        SELECT
            @rank := @rank + 1 AS rank,
            s.uid AS uid,
            s.coin AS coin
        FROM
            `user` s,
            (SELECT @rank := 0) r
        ORDER BY
            coin DESC,
            create_time
    ) q
WHERE
    q.uid = 'xiaoming';

根据 select @rank 与 user表结合起来作为一张有排名的新表,然后再从中找出某个用户的排名。这种方法的优点是简单,每次用户来请求,只要用这个SQL查一下即可;缺点是这个算是比较复杂的SQL,查起来太慢,每次都要全表查询,试了几次都要0.5s左右。用explain分析如下:


explain
2. user中添加rank字段

在user中添加rank字段,写个计划任务每隔2分钟全表扫描,然后更新rank名次字段。这个方法最残暴,但是也是最不可取的。首先会产生延迟,因为2分钟才更新一次名次,其次,每次都要更新全部数据,给数据库很大的压力,最后,计划任务更新数据的时候,用户送礼也在更新数据,稍微不注意就会出现脏读的情况。

3. 用Redis的zset数据结构
  • ZSet实现排行榜

zset的相关api (PipelineCluster/Jedis)
  1. 插入或者更新数据
    Long zadd(final String key, final double score, final String member)
    key : 排行榜的名字
    memeber : 用户
    score : 用户的分数
  2. 获取用户分数
    Double zscore(String key, final String member)
  3. 获取用户的排名
    Long zrevrank(final String key, final String member):(score从大到小,从0开始,所以需要加1)
    Long zrank(final String key, final String member):(score从小到大,从0开始,所以需要加1)
  4. 获取某个范围内的用户排名
    Set<Tuple> zrevrangeWithScoresBytes(String key, final long start, final long end) (从大到小)
    Set<Tuple> zrangeWithScoresBytes(String key, final long start, final long end) (从小到大)
    start : 开始排名
    end : 结束排名
    Tuple :
public class Tuple implements Comparable<Tuple> {
    // 用户
    private byte[] element;
    //分数
    private Double score;
}

比如我们想查1-10的排名,我们可以zrevrangeWithScoresBytes(key, 0, 9)

排行榜的实现
  1. 简单
    简单的排行榜就是每次用户信息更新后,把用户uid和用户coin都更新到zset中,这个的好处是比较简单,有一点不好的就是他不能实现先到先得,即先相同金额送礼越早越靠前。
  2. 较复杂(可实现先到先得)
    /**
     * 将coin加密成可以存在zset的值,实际上就是 coin * 10000000 + now % 10000000
     * @param coin
     * @return
     */
    public static Double encrypt(Long coin){
        Long value = coin * KEY + (KEY - DateUtil.getInt() % KEY);
        return value.doubleValue();
    }

    /**
     * 将zset的值转成long型的coin
      * @param value
     * @return
     */
    public static Long decrypt(Double value){
        Double coin = value / KEY;
        return coin.longValue();
    }
上一篇 下一篇

猜你喜欢

热点阅读