運營類的活動中,經常會有類似積分榜、排行榜的功能需求,實時的展現總的積分、排名,包括他們各自的基本信息,以及自己的積分排名等。
關係型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();
}