Redis用ZSet实现实时积分排行榜
背景
需求:实时积分排行榜
要求实时性,有序性
为什么要用Redis的ZSet
Redis的ZSet就像一个巨型的的电子记分牌,上面会有所有用户的积分和排名情况
ZSet是由Redis提供的一种数据结构。他存储一组唯一的成员,每个成员关联一个分数。成员按分数自动排序。
而使用数据库(如MySQL),一是实时性低,二是复杂度高,需要写sql,最后就是高并发能力差。
核心操作
- ZADD key score member : 添加或更新成员及其分数
- ZINCRBY key increment member : 给指定成员分数增加(可正可负)分数。(最常用)
- ZREVRANGE key start stop : 获取分数从高到底(REV是reverse)排名在[start,stop]区间内的成员
- ZREVRANK key member : 获取指定成员在从高到低排名中的名次(0表示第一名)
- ZSCORE key member : 获取指定成员的分数
- ZCARD key : 获取ZSet的成员数量
具体实现
- 配置RedisTemplate
@Configuration public class RedisConfig {
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer()); template.setEnableDefaultSerializer(true);
return template; } }
|
- 核心服务实现:
@Service public class RankingService {
private final RedisTemplate<String, Object> redisTemplate; private static final String RANKING_KEY = "game:leaderboard";
public RankingService(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; }
private ZSetOperations<String, Object> zSetOps() { return redisTemplate.opsForZSet(); }
public void addOrUpdateScore(String userId, double score) { zSetOps().incrementScore(RANKING_KEY, userId, score); }
public Double getScore(String userId) { return zSetOps().score(RANKING_KEY, userId); }
public Long getRank(String userId) { return zSetOps().reverseRank(RANKING_KEY, userId); }
public RankData getRankWithScore(String userId) { Long rank = zSetOps().reverseRank(RANKING_KEY, userId); Double score = zSetOps().score(RANKING_KEY, userId); if (rank == null || score == null) { return null; } return new RankData(userId, rank + 1, score); }
public Set<ZSetOperations.TypedTuple<Object>> getTopNRanks(int n) { return zSetOps().reverseRangeWithScores(RANKING_KEY, 0, n - 1); }
public Set<ZSetOperations.TypedTuple<Object>> getRanks(long start, long end) { return zSetOps().reverseRangeWithScores(RANKING_KEY, start, end); }
public Set<ZSetOperations.TypedTuple<Object>> getUsersByScoreRange(double minScore, double maxScore) { return zSetOps().rangeByScoreWithScores(RANKING_KEY, minScore, maxScore); }
@Data @AllArgsConstructor public static class RankData { private String userId; private Long rank; private Double score; } }
|
关于Jedis的拓展(老式)
是什么?
Jedis是最常用的Redis Java客户端之一
Redis服务器就相当于移动/联通的基站。这是一提供通信服务的核心设备,但它本身不能直接打电话
Java程序就相当于一个智能手机,想要使用基站的服务
Jedis就是手机里面的SIM卡和通信模块
简单来说,Jedis就是可以让Java程序说Redis语言,并与Redis服务器握手、会话的关键桥梁
具体使用
- Jedis类:代表一个到Redis服务器的单个链接。
- JedisPool类:管理多个Jedis实例,提供连接池功能。
关于RedisTemplate的拓展
RedisTemplate的使用