類似排行榜需求的實現

運營類的活動中,經常會有類似積分榜、排行榜的功能需求,實時的展現總的積分、排名,包括他們各自的基本信息,以及自己的積分排名等。

關係型DB對此的支持並不好。實現比較複雜而且會DB帶來不小的壓力。


之前對Redis Set操作使用比較多,包括smembers、scard、sadd、sdiff等。Redis的zset,即Sorted-Set,與set的區別在於zset加了一個分數(score)與之關聯。成員按照分數進行排序。和set一樣,成員必須唯一,但是分數可以重複。zset增刪改時間複雜度爲成員數量的對數,十分高效。


1. 使用API

<pre name="code" class="java">public class RedisUtils {
/**
   * 將 member及其 score加入到有序集 key當中.如 member已經是有序集的成員,那麼更新這個 member的score,並通過重新插入這個member來保證其在正確的位置上.
   * 如果 key不存在,則創建一個空的有序集並執行ZADD操作。當 key存在但不是有序集類型時返回一個錯誤
   * @param key
   * @param score
   * @param member
   * @return 如果是添加的新成員返回1;如果是成員本來就存在並更新成功返回0
   */
  Long zadd(String key, double score, String member);
  /**
   * 爲有序集key的成員member的score值加上增量increment
   * @param key
   * @param increment
   * @param member
   * @return 返回member成員的新score值
   */
  Double zincrby(String key, double increment, String member);
  /**
   * 返回有序集key中所有score值介於start與end之間(包括等於)的成員.成員按score值遞增(從小到大)排列
   * @param key
   * @param start
   * @param end
   * @return
   */
  Set<Tuple> zrangeWithScores(final String key, final int start, final int end);
  /**
   * 返回有序集key中所有score值介於start與end之間(包括等於)的成員.成員按score值遞減(從大到小)排列
   * @param key
   * @param start
   * @param end
   * @return
   */
  Set<Tuple> zrevrangeWithScores(final String key, final int start, final int end);
  /**
   * 返回有序集 key中,成員 member的 score值
   * @param key
   * @param member
   * @return
   */
  Double zscore(String key, String member);
  /**
   * 返回有序集key中成員member的排名.成員按 score值遞增(從大到小)順序排列
   * @param key
   * @param member
   * @return
   */
  Long zrevrank(String key, String member);
  /**
   * 移除多個元素
   * @param key
   * @param member
   * @return
   */
  Long zrem(String key, String... member);
}


2. 業務邏輯

  // fromPassport用戶給toUser用戶投了num票
  @Override
  public Map<String, Object> thumbsUp(String fromPassport, ProducerThumbsupUser toUser, int num) {


    logger.info(fromPassport + "-------------" + num);
    Map<String, Object> map = new HashMap<>();


    String toPassport = toUser.getPassport();


    ProducerThumbsupRecord producerThumbsupRecord =
        producerThumbsupRecordDao.getByFromAndToUser(fromPassport, toPassport);
    
    if (producerThumbsupRecord != null) {
      
      logger.info("producerThumbsupRecord" + producerThumbsupRecord);
      
      map.put("count", producerThumbsupRecord.getScore());
      map.put("already", true);


      return map;
    } else {
      producerThumbsupRecord = new ProducerThumbsupRecord();
      producerThumbsupRecord.setFromuser(fromPassport);
      producerThumbsupRecord.setTouser(toUser.getPassport());
      producerThumbsupRecord.setScore(num);
      producerThumbsupRecord.setCreatetime(new Date());
      int rtn = producerThumbsupRecordDao.save(producerThumbsupRecord);
      
      String userJson = JSON.toJSONString(toUser);

      double result = RedisUtils.zincrby(producerThumbsUpList, num, userJson);
      logger
          .info(
              "thumbsUp update fromPassport: {}, toPassport: {}, num: {}, userJson: {}, result: {}",
              fromPassport, toPassport, num, userJson, result);


      map.put("count", num);
      map.put("already", false);


      return map;
    }
  }

  // 排行榜
  @Override
  public List<ProducerThumbsupUserWithScore> getThumbsUpList() {


    List<ProducerThumbsupUserWithScore> list = new ArrayList<>();


    Set<Tuple> set = RedisUtils.zrevrangeWithScores(producerThumbsUpList, 0, 50);
    for (Tuple tuple : set) {
      logger.info(tuple.getScore() + "--------" + tuple.getElement());


      ProducerThumbsupUser user =
          JSONObject.parseObject(tuple.getElement(), ProducerThumbsupUser.class);
      int score = (int) tuple.getScore();
      list.add(new ProducerThumbsupUserWithScore(user, score));
    }


    return list;
  }
  // 用戶的票數
  @Override
  public int getScore(ProducerThumbsupUser producerThumbsupUser) {


    String user = JSON.toJSONString(producerThumbsupUser);
    logger.info("user--------" + user);
    Double score = RedisUtils.zscore(producerThumbsUpList, user);
    if (score == null) {
      return 0;
    }
    logger.info("score--------" + score);
    return score.intValue();
  }

  // 用戶的排名
  @Override
  public int getRank(ProducerThumbsupUser producerThumbsupUser) {


    String user = JSON.toJSONString(producerThumbsupUser);
    Long rank = RedisUtils.zrevrank(producerThumbsUpList, user);


    if (rank == null) {
      return 0;
    }


    return rank.intValue();
  }





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章