Redis使用场景
Redis使用场景
概述
Redis作为高性能的内存数据库,在现代应用架构中有着广泛的应用场景。本文详细介绍Redis在缓存、分布式锁、消息队列、计数器、会话存储等场景中的应用实践。
缓存应用
数据缓存
查询结果缓存
java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public User getUserById(Long userId) {
String cacheKey = "user:" + userId;
// 先从缓存获取
User user = (User) redisTemplate.opsForValue().get(cacheKey);
if (user != null) {
return user;
}
// 缓存未命中,从数据库查询
user = userRepository.findById(userId);
if (user != null) {
// 写入缓存,设置过期时间
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofHours(1));
}
return user;
}
public void updateUser(User user) {
// 更新数据库
userRepository.save(user);
// 删除缓存,保证数据一致性
String cacheKey = "user:" + user.getId();
redisTemplate.delete(cacheKey);
}
}
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public User getUserById(Long userId) {
String cacheKey = "user:" + userId;
// 先从缓存获取
User user = (User) redisTemplate.opsForValue().get(cacheKey);
if (user != null) {
return user;
}
// 缓存未命中,从数据库查询
user = userRepository.findById(userId);
if (user != null) {
// 写入缓存,设置过期时间
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofHours(1));
}
return user;
}
public void updateUser(User user) {
// 更新数据库
userRepository.save(user);
// 删除缓存,保证数据一致性
String cacheKey = "user:" + user.getId();
redisTemplate.delete(cacheKey);
}
}
页面缓存
java
@RestController
public class ProductController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@GetMapping("/products/{categoryId}")
public String getProductList(@PathVariable Long categoryId, HttpServletResponse response) {
String cacheKey = "product_list:" + categoryId;
// 检查缓存
String cachedHtml = redisTemplate.opsForValue().get(cacheKey);
if (cachedHtml != null) {
response.setHeader("X-Cache", "HIT");
return cachedHtml;
}
// 生成页面内容
String html = generateProductListHtml(categoryId);
// 缓存页面,设置5分钟过期
redisTemplate.opsForValue().set(cacheKey, html, Duration.ofMinutes(5));
response.setHeader("X-Cache", "MISS");
return html;
}
}
@RestController
public class ProductController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@GetMapping("/products/{categoryId}")
public String getProductList(@PathVariable Long categoryId, HttpServletResponse response) {
String cacheKey = "product_list:" + categoryId;
// 检查缓存
String cachedHtml = redisTemplate.opsForValue().get(cacheKey);
if (cachedHtml != null) {
response.setHeader("X-Cache", "HIT");
return cachedHtml;
}
// 生成页面内容
String html = generateProductListHtml(categoryId);
// 缓存页面,设置5分钟过期
redisTemplate.opsForValue().set(cacheKey, html, Duration.ofMinutes(5));
response.setHeader("X-Cache", "MISS");
return html;
}
}
缓存策略
Cache-Aside模式
java
@Component
public class CacheAsidePattern {
// 读取数据
public User getUser(Long userId) {
// 1. 先读缓存
User user = getFromCache(userId);
if (user != null) {
return user;
}
// 2. 缓存未命中,读数据库
user = getFromDatabase(userId);
if (user != null) {
// 3. 写入缓存
putToCache(userId, user);
}
return user;
}
// 更新数据
public void updateUser(User user) {
// 1. 先更新数据库
updateDatabase(user);
// 2. 删除缓存
deleteFromCache(user.getId());
}
}
@Component
public class CacheAsidePattern {
// 读取数据
public User getUser(Long userId) {
// 1. 先读缓存
User user = getFromCache(userId);
if (user != null) {
return user;
}
// 2. 缓存未命中,读数据库
user = getFromDatabase(userId);
if (user != null) {
// 3. 写入缓存
putToCache(userId, user);
}
return user;
}
// 更新数据
public void updateUser(User user) {
// 1. 先更新数据库
updateDatabase(user);
// 2. 删除缓存
deleteFromCache(user.getId());
}
}
Write-Through模式
java
@Component
public class WriteThroughPattern {
public void updateUser(User user) {
// 同时更新缓存和数据库
updateCache(user);
updateDatabase(user);
}
}
@Component
public class WriteThroughPattern {
public void updateUser(User user) {
// 同时更新缓存和数据库
updateCache(user);
updateDatabase(user);
}
}
Write-Behind模式
java
@Component
public class WriteBehindPattern {
private final Queue<User> writeQueue = new ConcurrentLinkedQueue<>();
public void updateUser(User user) {
// 1. 立即更新缓存
updateCache(user);
// 2. 异步更新数据库
writeQueue.offer(user);
}
@Scheduled(fixedDelay = 1000)
public void flushToDatabase() {
List<User> batch = new ArrayList<>();
User user;
while ((user = writeQueue.poll()) != null && batch.size() < 100) {
batch.add(user);
}
if (!batch.isEmpty()) {
batchUpdateDatabase(batch);
}
}
}
@Component
public class WriteBehindPattern {
private final Queue<User> writeQueue = new ConcurrentLinkedQueue<>();
public void updateUser(User user) {
// 1. 立即更新缓存
updateCache(user);
// 2. 异步更新数据库
writeQueue.offer(user);
}
@Scheduled(fixedDelay = 1000)
public void flushToDatabase() {
List<User> batch = new ArrayList<>();
User user;
while ((user = writeQueue.poll()) != null && batch.size() < 100) {
batch.add(user);
}
if (!batch.isEmpty()) {
batchUpdateDatabase(batch);
}
}
}
分布式锁
简单分布式锁
java
@Component
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public boolean tryLock(String lockKey, String requestId, long expireTime) {
// 使用SET命令的NX和EX参数实现原子操作
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, Duration.ofMillis(expireTime));
return Boolean.TRUE.equals(result);
}
public boolean releaseLock(String lockKey, String requestId) {
// 使用Lua脚本保证原子性
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
Long result = redisTemplate.execute(
RedisScript.of(script, Long.class),
Collections.singletonList(lockKey),
requestId
);
return Long.valueOf(1).equals(result);
}
}
// 使用示例
@Service
public class OrderService {
@Autowired
private RedisDistributedLock distributedLock;
public void processOrder(String orderId) {
String lockKey = "order_lock:" + orderId;
String requestId = UUID.randomUUID().toString();
if (distributedLock.tryLock(lockKey, requestId, 30000)) {
try {
// 处理订单逻辑
doProcessOrder(orderId);
} finally {
distributedLock.releaseLock(lockKey, requestId);
}
} else {
throw new RuntimeException("获取锁失败,订单正在处理中");
}
}
}
@Component
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public boolean tryLock(String lockKey, String requestId, long expireTime) {
// 使用SET命令的NX和EX参数实现原子操作
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, Duration.ofMillis(expireTime));
return Boolean.TRUE.equals(result);
}
public boolean releaseLock(String lockKey, String requestId) {
// 使用Lua脚本保证原子性
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
Long result = redisTemplate.execute(
RedisScript.of(script, Long.class),
Collections.singletonList(lockKey),
requestId
);
return Long.valueOf(1).equals(result);
}
}
// 使用示例
@Service
public class OrderService {
@Autowired
private RedisDistributedLock distributedLock;
public void processOrder(String orderId) {
String lockKey = "order_lock:" + orderId;
String requestId = UUID.randomUUID().toString();
if (distributedLock.tryLock(lockKey, requestId, 30000)) {
try {
// 处理订单逻辑
doProcessOrder(orderId);
} finally {
distributedLock.releaseLock(lockKey, requestId);
}
} else {
throw new RuntimeException("获取锁失败,订单正在处理中");
}
}
}
可重入分布式锁
java
@Component
public class ReentrantRedisLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private final ThreadLocal<Map<String, Integer>> lockCount = new ThreadLocal<>();
public boolean lock(String lockKey, long expireTime) {
String threadId = Thread.currentThread().getId() + "";
// 检查当前线程是否已持有锁
Map<String, Integer> counts = lockCount.get();
if (counts != null && counts.containsKey(lockKey)) {
counts.put(lockKey, counts.get(lockKey) + 1);
return true;
}
// 尝试获取锁
Boolean acquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, threadId, Duration.ofMillis(expireTime));
if (Boolean.TRUE.equals(acquired)) {
if (counts == null) {
counts = new HashMap<>();
lockCount.set(counts);
}
counts.put(lockKey, 1);
return true;
}
return false;
}
public void unlock(String lockKey) {
Map<String, Integer> counts = lockCount.get();
if (counts == null || !counts.containsKey(lockKey)) {
return;
}
int count = counts.get(lockKey);
if (count > 1) {
counts.put(lockKey, count - 1);
} else {
counts.remove(lockKey);
if (counts.isEmpty()) {
lockCount.remove();
}
// 释放Redis锁
String threadId = Thread.currentThread().getId() + "";
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
redisTemplate.execute(
RedisScript.of(script, Long.class),
Collections.singletonList(lockKey),
threadId
);
}
}
}
@Component
public class ReentrantRedisLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private final ThreadLocal<Map<String, Integer>> lockCount = new ThreadLocal<>();
public boolean lock(String lockKey, long expireTime) {
String threadId = Thread.currentThread().getId() + "";
// 检查当前线程是否已持有锁
Map<String, Integer> counts = lockCount.get();
if (counts != null && counts.containsKey(lockKey)) {
counts.put(lockKey, counts.get(lockKey) + 1);
return true;
}
// 尝试获取锁
Boolean acquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, threadId, Duration.ofMillis(expireTime));
if (Boolean.TRUE.equals(acquired)) {
if (counts == null) {
counts = new HashMap<>();
lockCount.set(counts);
}
counts.put(lockKey, 1);
return true;
}
return false;
}
public void unlock(String lockKey) {
Map<String, Integer> counts = lockCount.get();
if (counts == null || !counts.containsKey(lockKey)) {
return;
}
int count = counts.get(lockKey);
if (count > 1) {
counts.put(lockKey, count - 1);
} else {
counts.remove(lockKey);
if (counts.isEmpty()) {
lockCount.remove();
}
// 释放Redis锁
String threadId = Thread.currentThread().getId() + "";
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
redisTemplate.execute(
RedisScript.of(script, Long.class),
Collections.singletonList(lockKey),
threadId
);
}
}
}
消息队列
简单消息队列
java
@Component
public class RedisMessageQueue {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 生产者
public void sendMessage(String queue, String message) {
redisTemplate.opsForList().leftPush(queue, message);
}
// 消费者(阻塞)
public String receiveMessage(String queue, long timeout) {
List<String> result = redisTemplate.opsForList()
.rightPop(queue, Duration.ofSeconds(timeout));
return result != null && !result.isEmpty() ? result.get(0) : null;
}
// 消费者(非阻塞)
public String receiveMessageNonBlocking(String queue) {
return redisTemplate.opsForList().rightPop(queue);
}
}
@Component
public class RedisMessageQueue {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 生产者
public void sendMessage(String queue, String message) {
redisTemplate.opsForList().leftPush(queue, message);
}
// 消费者(阻塞)
public String receiveMessage(String queue, long timeout) {
List<String> result = redisTemplate.opsForList()
.rightPop(queue, Duration.ofSeconds(timeout));
return result != null && !result.isEmpty() ? result.get(0) : null;
}
// 消费者(非阻塞)
public String receiveMessageNonBlocking(String queue) {
return redisTemplate.opsForList().rightPop(queue);
}
}
延迟队列
java
@Component
public class RedisDelayQueue {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 添加延迟消息
public void addDelayMessage(String queue, String message, long delaySeconds) {
long executeTime = System.currentTimeMillis() + delaySeconds * 1000;
redisTemplate.opsForZSet().add(queue, message, executeTime);
}
// 获取到期消息
@Scheduled(fixedDelay = 1000)
public void processDelayMessages() {
String queue = "delay_queue";
long currentTime = System.currentTimeMillis();
Set<String> messages = redisTemplate.opsForZSet()
.rangeByScore(queue, 0, currentTime);
for (String message : messages) {
// 处理消息
processMessage(message);
// 删除已处理的消息
redisTemplate.opsForZSet().remove(queue, message);
}
}
}
@Component
public class RedisDelayQueue {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 添加延迟消息
public void addDelayMessage(String queue, String message, long delaySeconds) {
long executeTime = System.currentTimeMillis() + delaySeconds * 1000;
redisTemplate.opsForZSet().add(queue, message, executeTime);
}
// 获取到期消息
@Scheduled(fixedDelay = 1000)
public void processDelayMessages() {
String queue = "delay_queue";
long currentTime = System.currentTimeMillis();
Set<String> messages = redisTemplate.opsForZSet()
.rangeByScore(queue, 0, currentTime);
for (String message : messages) {
// 处理消息
processMessage(message);
// 删除已处理的消息
redisTemplate.opsForZSet().remove(queue, message);
}
}
}
发布订阅
java
@Component
public class RedisPublisher {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public void publishMessage(String channel, String message) {
redisTemplate.convertAndSend(channel, message);
}
}
@Component
public class RedisSubscriber {
@RedisMessageListener(channels = "user_events")
public void handleUserEvent(String message) {
System.out.println("Received user event: " + message);
// 处理用户事件
}
@RedisMessageListener(patterns = "order_*")
public void handleOrderEvent(String message, String channel) {
System.out.println("Received order event on " + channel + ": " + message);
// 处理订单事件
}
}
@Component
public class RedisPublisher {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public void publishMessage(String channel, String message) {
redisTemplate.convertAndSend(channel, message);
}
}
@Component
public class RedisSubscriber {
@RedisMessageListener(channels = "user_events")
public void handleUserEvent(String message) {
System.out.println("Received user event: " + message);
// 处理用户事件
}
@RedisMessageListener(patterns = "order_*")
public void handleOrderEvent(String message, String channel) {
System.out.println("Received order event on " + channel + ": " + message);
// 处理订单事件
}
}
计数器和统计
访问计数
java
@Component
public class RedisCounter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 增加访问次数
public long incrementPageView(String pageId) {
String key = "page_views:" + pageId;
return redisTemplate.opsForValue().increment(key);
}
// 获取访问次数
public long getPageViews(String pageId) {
String key = "page_views:" + pageId;
String count = redisTemplate.opsForValue().get(key);
return count != null ? Long.parseLong(count) : 0;
}
// 每日访问统计
public long incrementDailyPageView(String pageId) {
String today = LocalDate.now().toString();
String key = "daily_page_views:" + today + ":" + pageId;
Long count = redisTemplate.opsForValue().increment(key);
// 设置过期时间(保留30天)
redisTemplate.expire(key, Duration.ofDays(30));
return count;
}
}
@Component
public class RedisCounter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 增加访问次数
public long incrementPageView(String pageId) {
String key = "page_views:" + pageId;
return redisTemplate.opsForValue().increment(key);
}
// 获取访问次数
public long getPageViews(String pageId) {
String key = "page_views:" + pageId;
String count = redisTemplate.opsForValue().get(key);
return count != null ? Long.parseLong(count) : 0;
}
// 每日访问统计
public long incrementDailyPageView(String pageId) {
String today = LocalDate.now().toString();
String key = "daily_page_views:" + today + ":" + pageId;
Long count = redisTemplate.opsForValue().increment(key);
// 设置过期时间(保留30天)
redisTemplate.expire(key, Duration.ofDays(30));
return count;
}
}
限流器
java
@Component
public class RedisRateLimiter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 固定窗口限流
public boolean isAllowed(String key, int limit, Duration window) {
String windowKey = key + ":" + (System.currentTimeMillis() / window.toMillis());
Long count = redisTemplate.opsForValue().increment(windowKey);
if (count == 1) {
redisTemplate.expire(windowKey, window);
}
return count <= limit;
}
// 滑动窗口限流
public boolean isAllowedSlidingWindow(String key, int limit, Duration window) {
long now = System.currentTimeMillis();
long windowStart = now - window.toMillis();
// 删除窗口外的记录
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
// 检查当前窗口内的请求数
Long count = redisTemplate.opsForZSet().count(key, windowStart, now);
if (count < limit) {
// 添加当前请求
redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);
redisTemplate.expire(key, window);
return true;
}
return false;
}
}
@Component
public class RedisRateLimiter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 固定窗口限流
public boolean isAllowed(String key, int limit, Duration window) {
String windowKey = key + ":" + (System.currentTimeMillis() / window.toMillis());
Long count = redisTemplate.opsForValue().increment(windowKey);
if (count == 1) {
redisTemplate.expire(windowKey, window);
}
return count <= limit;
}
// 滑动窗口限流
public boolean isAllowedSlidingWindow(String key, int limit, Duration window) {
long now = System.currentTimeMillis();
long windowStart = now - window.toMillis();
// 删除窗口外的记录
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
// 检查当前窗口内的请求数
Long count = redisTemplate.opsForZSet().count(key, windowStart, now);
if (count < limit) {
// 添加当前请求
redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);
redisTemplate.expire(key, window);
return true;
}
return false;
}
}
会话存储
Session管理
java
@Component
public class RedisSessionManager {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final Duration SESSION_TIMEOUT = Duration.ofMinutes(30);
public void createSession(String sessionId, Map<String, Object> attributes) {
String key = "session:" + sessionId;
redisTemplate.opsForHash().putAll(key, attributes);
redisTemplate.expire(key, SESSION_TIMEOUT);
}
public void setAttribute(String sessionId, String attribute, Object value) {
String key = "session:" + sessionId;
redisTemplate.opsForHash().put(key, attribute, value);
redisTemplate.expire(key, SESSION_TIMEOUT);
}
public Object getAttribute(String sessionId, String attribute) {
String key = "session:" + sessionId;
return redisTemplate.opsForHash().get(key, attribute);
}
public void removeSession(String sessionId) {
String key = "session:" + sessionId;
redisTemplate.delete(key);
}
public boolean isSessionValid(String sessionId) {
String key = "session:" + sessionId;
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
}
@Component
public class RedisSessionManager {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final Duration SESSION_TIMEOUT = Duration.ofMinutes(30);
public void createSession(String sessionId, Map<String, Object> attributes) {
String key = "session:" + sessionId;
redisTemplate.opsForHash().putAll(key, attributes);
redisTemplate.expire(key, SESSION_TIMEOUT);
}
public void setAttribute(String sessionId, String attribute, Object value) {
String key = "session:" + sessionId;
redisTemplate.opsForHash().put(key, attribute, value);
redisTemplate.expire(key, SESSION_TIMEOUT);
}
public Object getAttribute(String sessionId, String attribute) {
String key = "session:" + sessionId;
return redisTemplate.opsForHash().get(key, attribute);
}
public void removeSession(String sessionId) {
String key = "session:" + sessionId;
redisTemplate.delete(key);
}
public boolean isSessionValid(String sessionId) {
String key = "session:" + sessionId;
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
}
单点登录(SSO)
java
@Component
public class RedisSSOManager {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public String createSSOToken(String userId) {
String token = UUID.randomUUID().toString();
String key = "sso_token:" + token;
// 存储token到用户ID的映射
redisTemplate.opsForValue().set(key, userId, Duration.ofHours(8));
// 存储用户ID到token的映射(用于踢出其他会话)
String userKey = "user_token:" + userId;
String oldToken = redisTemplate.opsForValue().get(userKey);
if (oldToken != null) {
redisTemplate.delete("sso_token:" + oldToken);
}
redisTemplate.opsForValue().set(userKey, token, Duration.ofHours(8));
return token;
}
public String validateToken(String token) {
String key = "sso_token:" + token;
return redisTemplate.opsForValue().get(key);
}
public void logout(String token) {
String key = "sso_token:" + token;
String userId = redisTemplate.opsForValue().get(key);
if (userId != null) {
redisTemplate.delete(key);
redisTemplate.delete("user_token:" + userId);
}
}
}
@Component
public class RedisSSOManager {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public String createSSOToken(String userId) {
String token = UUID.randomUUID().toString();
String key = "sso_token:" + token;
// 存储token到用户ID的映射
redisTemplate.opsForValue().set(key, userId, Duration.ofHours(8));
// 存储用户ID到token的映射(用于踢出其他会话)
String userKey = "user_token:" + userId;
String oldToken = redisTemplate.opsForValue().get(userKey);
if (oldToken != null) {
redisTemplate.delete("sso_token:" + oldToken);
}
redisTemplate.opsForValue().set(userKey, token, Duration.ofHours(8));
return token;
}
public String validateToken(String token) {
String key = "sso_token:" + token;
return redisTemplate.opsForValue().get(key);
}
public void logout(String token) {
String key = "sso_token:" + token;
String userId = redisTemplate.opsForValue().get(key);
if (userId != null) {
redisTemplate.delete(key);
redisTemplate.delete("user_token:" + userId);
}
}
}
排行榜和推荐
实时排行榜
java
@Component
public class RedisLeaderboard {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 更新用户分数
public void updateScore(String leaderboard, String userId, double score) {
redisTemplate.opsForZSet().add(leaderboard, userId, score);
}
// 增加用户分数
public void incrementScore(String leaderboard, String userId, double increment) {
redisTemplate.opsForZSet().incrementScore(leaderboard, userId, increment);
}
// 获取排行榜前N名
public List<String> getTopN(String leaderboard, int n) {
Set<String> topUsers = redisTemplate.opsForZSet()
.reverseRange(leaderboard, 0, n - 1);
return new ArrayList<>(topUsers);
}
// 获取用户排名
public Long getUserRank(String leaderboard, String userId) {
return redisTemplate.opsForZSet().reverseRank(leaderboard, userId);
}
// 获取用户分数
public Double getUserScore(String leaderboard, String userId) {
return redisTemplate.opsForZSet().score(leaderboard, userId);
}
// 获取分数范围内的用户
public Set<String> getUsersByScoreRange(String leaderboard, double min, double max) {
return redisTemplate.opsForZSet().rangeByScore(leaderboard, min, max);
}
}
@Component
public class RedisLeaderboard {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 更新用户分数
public void updateScore(String leaderboard, String userId, double score) {
redisTemplate.opsForZSet().add(leaderboard, userId, score);
}
// 增加用户分数
public void incrementScore(String leaderboard, String userId, double increment) {
redisTemplate.opsForZSet().incrementScore(leaderboard, userId, increment);
}
// 获取排行榜前N名
public List<String> getTopN(String leaderboard, int n) {
Set<String> topUsers = redisTemplate.opsForZSet()
.reverseRange(leaderboard, 0, n - 1);
return new ArrayList<>(topUsers);
}
// 获取用户排名
public Long getUserRank(String leaderboard, String userId) {
return redisTemplate.opsForZSet().reverseRank(leaderboard, userId);
}
// 获取用户分数
public Double getUserScore(String leaderboard, String userId) {
return redisTemplate.opsForZSet().score(leaderboard, userId);
}
// 获取分数范围内的用户
public Set<String> getUsersByScoreRange(String leaderboard, double min, double max) {
return redisTemplate.opsForZSet().rangeByScore(leaderboard, min, max);
}
}
地理位置应用
附近的人
java
@Component
public class RedisGeoService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 添加用户位置
public void addUserLocation(String userId, double longitude, double latitude) {
redisTemplate.opsForGeo().add("user_locations",
new Point(longitude, latitude), userId);
}
// 查找附近的用户
public List<String> findNearbyUsers(String userId, double radius) {
// 先获取用户当前位置
List<Point> positions = redisTemplate.opsForGeo()
.position("user_locations", userId);
if (positions.isEmpty()) {
return Collections.emptyList();
}
Point userPosition = positions.get(0);
// 查找半径内的用户
GeoResults<RedisGeoCommands.GeoLocation<String>> results =
redisTemplate.opsForGeo().radius("user_locations",
new Circle(userPosition, new Distance(radius, Metrics.KILOMETERS)));
return results.getContent().stream()
.map(result -> result.getContent().getName())
.filter(name -> !name.equals(userId)) // 排除自己
.collect(Collectors.toList());
}
// 计算两用户间距离
public Distance getDistanceBetweenUsers(String userId1, String userId2) {
return redisTemplate.opsForGeo()
.distance("user_locations", userId1, userId2, Metrics.KILOMETERS);
}
}
@Component
public class RedisGeoService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 添加用户位置
public void addUserLocation(String userId, double longitude, double latitude) {
redisTemplate.opsForGeo().add("user_locations",
new Point(longitude, latitude), userId);
}
// 查找附近的用户
public List<String> findNearbyUsers(String userId, double radius) {
// 先获取用户当前位置
List<Point> positions = redisTemplate.opsForGeo()
.position("user_locations", userId);
if (positions.isEmpty()) {
return Collections.emptyList();
}
Point userPosition = positions.get(0);
// 查找半径内的用户
GeoResults<RedisGeoCommands.GeoLocation<String>> results =
redisTemplate.opsForGeo().radius("user_locations",
new Circle(userPosition, new Distance(radius, Metrics.KILOMETERS)));
return results.getContent().stream()
.map(result -> result.getContent().getName())
.filter(name -> !name.equals(userId)) // 排除自己
.collect(Collectors.toList());
}
// 计算两用户间距离
public Distance getDistanceBetweenUsers(String userId1, String userId2) {
return redisTemplate.opsForGeo()
.distance("user_locations", userId1, userId2, Metrics.KILOMETERS);
}
}
最佳实践
1. 键命名规范
- 使用有意义的前缀:
user:1001:profile
- 使用冒号分隔层级:
cache:product:category:electronics
- 避免过长的键名,影响内存使用
2. 过期时间设置
- 根据业务需求设置合理的过期时间
- 使用随机过期时间避免缓存雪崩
- 定期清理无用的键
3. 内存优化
- 选择合适的数据结构
- 使用压缩配置减少内存占用
- 监控内存使用情况
4. 性能优化
- 使用pipeline批量操作
- 避免大key,影响性能
- 合理使用连接池
总结
Redis在现代应用中有着广泛的应用场景,从简单的缓存到复杂的分布式系统组件。正确理解和使用Redis的各种数据结构和特性,能够有效解决许多技术问题,提升系统的性能和可扩展性。在实际应用中,需要根据具体业务需求选择合适的使用模式,并注意性能优化和最佳实践。