redis(zset使用)使用场景案例
1.排行榜系统(游戏积分榜)
java
@Service
public class GameRankingService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String RANKING_KEY = "game:ranking";
// 更新玩家分数
public void updatePlayerScore(String playerId, double score) {
redisTemplate.opsForZSet().add(RANKING_KEY, playerId, score);
}
// 获取玩家排名(从高到低)
public Long getPlayerRank(String playerId) {
// ZREVRANK 获取从高到低的排名
return redisTemplate.opsForZSet().reverseRank(RANKING_KEY, playerId);
}
// 获取排行榜前N名
public Set<ZSetOperations.TypedTuple<Object>> getTopPlayers(int topN) {
return redisTemplate.opsForZSet().reverseRangeWithScores(RANKING_KEY, 0, topN - 1);
}
// 获取玩家分数
public Double getPlayerScore(String playerId) {
return redisTemplate.opsForZSet().score(RANKING_KEY, playerId);
}
}
2.延迟队列
java
@Service
public class DelayTaskService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String DELAY_QUEUE_KEY = "delay:tasks";
// 添加延时任务
public void addDelayTask(String taskId, long delaySeconds) {
double executeTime = System.currentTimeMillis() + delaySeconds * 1000;
redisTemplate.opsForZSet().add(DELAY_QUEUE_KEY, taskId, executeTime);
}
// 扫描并执行到期任务
public void executeExpiredTasks() {
long currentTime = System.currentTimeMillis();
// 获取所有已到期的任务
Set<ZSetOperations.TypedTuple<Object>> expiredTasks =
redisTemplate.opsForZSet().rangeByScoreWithScores(DELAY_QUEUE_KEY, 0, currentTime);
for (ZSetOperations.TypedTuple<Object> task : expiredTasks) {
String taskId = (String) task.getValue();
// 执行任务逻辑
processTask(taskId);
// 从队列中移除已处理的任务
redisTemplate.opsForZSet().remove(DELAY_QUEUE_KEY, taskId);
}
}
private void processTask(String taskId) {
// 具体的任务处理逻辑
System.out.println("Processing task: " + taskId + " at " + System.currentTimeMillis());
}
}
3.热点数据统计
java
@Service
public class HotDataService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String HOT_ARTICLES_KEY = "hot:articles";
// 增加文章阅读量
public void incrementArticleView(Long articleId) {
redisTemplate.opsForZSet().incrementScore(HOT_ARTICLES_KEY,
String.valueOf(articleId), 1);
}
// 获取热门文章TopN
public Set<ZSetOperations.TypedTuple<Object>> getHotArticles(int topN) {
return redisTemplate.opsForZSet().reverseRangeWithScores(HOT_ARTICLES_KEY, 0, topN - 1);
}
// 获取文章排名
public Long getArticleRank(Long articleId) {
return redisTemplate.opsForZSet().reverseRank(HOT_ARTICLES_KEY,
String.valueOf(articleId));
}
}
4.时间轴实现
java
@Service
public class TimelineService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 添加动态到用户时间轴
public void addToTimeline(String userId, String postId, long timestamp) {
String timelineKey = "timeline:" + userId;
redisTemplate.opsForZSet().add(timelineKey, postId, timestamp);
// 限制时间轴长度,只保留最近1000条
redisTemplate.opsForZSet().removeRange(timelineKey, 0, -1001);
}
// 获取用户时间轴
public Set<Object> getTimeline(String userId, int page, int size) {
String timelineKey = "timeline:" + userId;
long start = (page - 1) * size;
long end = start + size - 1;
// 按时间倒序排列
return redisTemplate.opsForZSet().reverseRange(timelineKey, start, end);
}
// 删除时间轴中的某条动态
public void removeFromTimeline(String userId, String postId) {
String timelineKey = "timeline:" + userId;
redisTemplate.opsForZSet().remove(timelineKey, postId);
}
}
5.社交列表实现
@Service
public class SocialFollowService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 关注用户
*/
public void followUser(String followerId, String followeeId) {
String followKey = "user:" + followerId + ":follows";
double timestamp = System.currentTimeMillis();
redisTemplate.opsForZSet().add(followKey, followeeId, timestamp);
}
/**
* 取消关注
*/
public void unfollowUser(String followerId, String followeeId) {
String followKey = "user:" + followerId + ":follows";
redisTemplate.opsForZSet().remove(followKey, followeeId);
}
/**
* 获取最近关注的用户(按时间倒序)
*/
public Set<Object> getRecentFollows(String userId, int limit) {
String followKey = "user:" + userId + ":follows";
return redisTemplate.opsForZSet().reverseRange(followKey, 0, limit - 1);
}
/**
* 获取关注时间
*/
public Double getFollowTime(String followerId, String followeeId) {
String followKey = "user:" + followerId + ":follows";
return redisTemplate.opsForZSet().score(followKey, followeeId);
}
}
6.滑动窗口实现(限流)
@Service
public class SlidingWindowRateLimitService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 记录请求并清理过期数据
*/
public boolean allowRequest(String clientId, int windowSeconds, int maxRequests) {
String key = "rate:limit:" + clientId;
long currentTime = System.currentTimeMillis();
long windowStartTime = currentTime - windowSeconds * 1000L;
// 记录当前请求
redisTemplate.opsForZSet().add(key, String.valueOf(currentTime), currentTime);
// 清理窗口期之前的请求
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStartTime);
// 获取当前窗口内的请求数量
Long requestCount = redisTemplate.opsForZSet().zCard(key);
// 设置过期时间
redisTemplate.expire(key, windowSeconds, TimeUnit.SECONDS);
return requestCount != null && requestCount <= maxRequests;
}
/**
* 获取当前请求数量
*/
public Long getCurrentRequestCount(String clientId) {
String key = "rate:limit:" + clientId;
return redisTemplate.opsForZSet().zCard(key);
}
}
7.地理位置搜索
@Service
public class GeoLocationService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String USER_LOCATIONS_KEY = "user:locations";
/**
* 添加用户位置
* 将经纬度合并为一个唯一的分数
*/
public void addUserLocation(String userId, double latitude, double longitude) {
// 将经纬度编码为一个唯一的double值
double locationScore = encodeLocation(latitude, longitude);
redisTemplate.opsForZSet().add(USER_LOCATIONS_KEY, userId, locationScore);
}
/**
* 编码地理位置(简单实现)
*/
private double encodeLocation(double latitude, double longitude) {
// 将经纬度转换为固定格式的数字
// 例如:纬度40.7128,经度-74.0060 -> 407128740060
return latitude * 1000000 + longitude;
}
/**
* 查找附近用户(简化版本)
*/
public Set<Object> findNearbyUsers(double centerLat, double centerLng, double radius) {
double minScore = encodeLocation(centerLat - radius, centerLng - radius);
double maxScore = encodeLocation(centerLat + radius, centerLng + radius);
return redisTemplate.opsForZSet().rangeByScore(USER_LOCATIONS_KEY, minScore, maxScore);
}
/**
* 获取用户位置
*/
public Double getUserLocation(String userId) {
return redisTemplate.opsForZSet().score(USER_LOCATIONS_KEY, userId);
}
}