运营类的活动中,经常会有类似积分榜、排行榜的功能需求,实时的展现总的积分、排名,包括他们各自的基本信息,以及自己的积分排名等。
关系型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();
}