Redis 凭借其高性能和丰富的数据结构,在实际项目中有着广泛的应用场景。以下是 Redis 的主要应用场景及实现方式。
1. 缓存
应用场景
- 热点数据缓存:缓存频繁访问的数据,减轻数据库压力
- 查询结果缓存:缓存复杂查询的结果,提高查询性能
- 页面缓存:缓存页面渲染结果,减少服务器计算
实现方式
java// 读取缓存 public User getUserById(Long id) { String key = "user:" + id; User user = redis.get(key); if (user != null) { return user; } // 缓存未命中,查询数据库 user = db.queryUserById(id); // 写入缓存 redis.set(key, user, 3600); // 缓存 1 小时 return user; } // 更新缓存 public void updateUser(User user) { db.updateUser(user); redis.del("user:" + user.getId()); // 删除缓存 }
注意事项
- 设置合理的过期时间,避免缓存雪崩
- 使用缓存穿透、缓存击穿、缓存雪崩的解决方案
- 监控缓存命中率,及时调整缓存策略
2. 会话存储
应用场景
- 用户登录状态:存储用户的登录状态和会话信息
- 分布式会话:在分布式系统中共享会话信息
- 临时数据存储:存储临时数据,如验证码、临时令牌等
实现方式
java// 用户登录 public String login(String username, String password) { User user = db.authenticate(username, password); if (user != null) { String sessionId = generateSessionId(); String key = "session:" + sessionId; // 存储会话信息 redis.set(key, user, 1800); // 30 分钟过期 return sessionId; } return null; } // 验证会话 public User validateSession(String sessionId) { String key = "session:" + sessionId; User user = redis.get(key); if (user != null) { // 刷新过期时间 redis.expire(key, 1800); return user; } return null; }
3. 计数器
应用场景
- 文章阅读量:统计文章的阅读次数
- 视频播放量:统计视频的播放次数
- 点赞数:统计点赞数量
- 访问量统计:统计网站访问量
实现方式
java// 增加计数 public long incrementViewCount(Long articleId) { String key = "article:view:" + articleId; return redis.incr(key); } // 获取计数 public long getViewCount(Long articleId) { String key = "article:view:" + articleId; return redis.get(key); } // 批量获取计数 public Map<Long, Long> getViewCounts(List<Long> articleIds) { String[] keys = articleIds.stream() .map(id -> "article:view:" + id) .toArray(String[]::new); return redis.mget(keys); }
4. 排行榜
应用场景
- 游戏排行榜:实时显示玩家排名
- 文章排行榜:显示热门文章排行
- 商品销量排行:显示商品销量排行
- 用户积分排行:显示用户积分排行
实现方式
java// 增加分数 public void addScore(Long userId, double score) { String key = "leaderboard:user"; redis.zadd(key, score, userId.toString()); } // 获取排行榜 public List<User> getLeaderboard(int start, int end) { String key = "leaderboard:user"; // 获取排名和分数 Set<Tuple> tuples = redis.zrevrangeWithScores(key, start, end); // 转换为用户列表 return tuples.stream() .map(tuple -> { Long userId = Long.parseLong(tuple.getElement()); User user = db.getUserById(userId); user.setScore(tuple.getScore()); user.setRank(redis.zrevrank(key, userId.toString()) + 1); return user; }) .collect(Collectors.toList()); } // 获取用户排名 public long getUserRank(Long userId) { String key = "leaderboard:user"; Long rank = redis.zrevrank(key, userId.toString()); return rank != null ? rank + 1 : 0; }
5. 消息队列
应用场景
- 异步任务:将耗时任务放入队列,异步处理
- 任务调度:实现定时任务和任务调度
- 事件通知:实现发布订阅模式
- 日志收集:收集和分发日志
实现方式
java// 生产者:发送消息 public void sendMessage(String queue, String message) { redis.lpush(queue, message); } // 消费者:消费消息 public String consumeMessage(String queue) { return redis.brpop(queue, 0); // 阻塞式消费 } // 批量消费 public List<String> consumeMessages(String queue, int count) { List<String> messages = new ArrayList<>(); for (int i = 0; i < count; i++) { String message = redis.rpop(queue); if (message == null) { break; } messages.add(message); } return messages; }
6. 分布式锁
应用场景
- 库存扣减:防止超卖
- 订单创建:防止重复创建订单
- 资源竞争:解决并发竞争问题
- 限流控制:实现接口限流
实现方式
java// 获取锁 public boolean tryLock(String key, String value, int expireTime) { String result = redis.set(key, value, "NX", "EX", expireTime); return "OK".equals(result); } // 释放锁 public void unlock(String key, String value) { String script = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end"; redis.eval(script, Collections.singletonList(key), Collections.singletonList(value)); } // 使用锁 public void deductStock(Long productId, int quantity) { String lockKey = "lock:product:" + productId; String lockValue = UUID.randomUUID().toString(); try { // 获取锁 if (tryLock(lockKey, lockValue, 10)) { // 扣减库存 db.deductStock(productId, quantity); } } finally { // 释放锁 unlock(lockKey, lockValue); } }
7. 限流器
应用场景
- API 限流:限制 API 调用频率
- 防刷接口:防止恶意刷接口
- 用户限流:限制用户操作频率
- 系统保护:保护系统不被过载
实现方式
java// 固定窗口限流 public boolean allowRequest(String key, int limit, int expireTime) { String count = redis.get(key); if (count == null) { redis.set(key, "1", expireTime); return true; } int currentCount = Integer.parseInt(count); if (currentCount < limit) { redis.incr(key); return true; } return false; } // 滑动窗口限流 public boolean allowRequestSliding(String key, int limit, int windowSize) { long currentTime = System.currentTimeMillis(); long windowStart = currentTime - windowSize; // 移除窗口外的数据 redis.zremrangeByScore(key, 0, windowStart); // 添加当前请求 redis.zadd(key, currentTime, UUID.randomUUID().toString()); // 统计窗口内的请求数 long count = redis.zcard(key); return count <= limit; }
8. 标签系统
应用场景
- 文章标签:为文章添加标签
- 用户标签:为用户添加标签
- 标签查询:根据标签查询内容
- 标签推荐:基于标签推荐内容
实现方式
java// 添加标签 public void addTags(Long articleId, Set<String> tags) { String key = "article:tags:" + articleId; redis.sadd(key, tags.toArray(new String[0])); } // 获取标签 public Set<String> getTags(Long articleId) { String key = "article:tags:" + articleId; return redis.smembers(key); } // 根据标签查询文章 public Set<Long> getArticlesByTag(String tag) { String key = "tag:articles:" + tag; return redis.smembers(key); } // 获取共同标签 public Set<String> getCommonTags(Long articleId1, Long articleId2) { String key1 = "article:tags:" + articleId1; String key2 = "article:tags:" + articleId2; return redis.sinter(key1, key2); }
9. 地理位置
应用场景
- 附近的人:查找附近的人
- 附近的位置:查找附近的位置
- 距离计算:计算两个位置的距离
- 位置服务:提供位置相关服务
实现方式
java// 添加位置 public void addLocation(String key, double longitude, double latitude) { redis.geoadd(key, longitude, latitude, key); } // 查找附近的位置 public List<Location> getNearbyLocations(String key, double longitude, double latitude, double radius) { return redis.georadius(key, longitude, latitude, radius, GeoUnit.KM); } // 计算距离 public double getDistance(String key, String member1, String member2) { return redis.geodist(key, member1, member2, GeoUnit.KM); }
10. 实时统计
应用场景
- 在线人数:统计在线用户数
- UV 统计:统计独立访客数
- PV 统计:统计页面浏览量
- 实时数据:实时统计业务数据
实现方式
java// UV 统计(使用 HyperLogLog) public long getUV(String key) { return redis.pfcount(key); } public void addUV(String key, String userId) { redis.pfadd(key, userId); } // PV 统计(使用计数器) public long getPV(String key) { String count = redis.get(key); return count != null ? Long.parseLong(count) : 0; } public void addPV(String key) { redis.incr(key); }
总结
Redis 在实际项目中有广泛的应用场景,包括缓存、会话存储、计数器、排行榜、消息队列、分布式锁、限流器、标签系统、地理位置、实时统计等。每个应用场景都有其特定的实现方式和注意事项。在实际开发中,需要根据具体的业务需求,选择合适的应用场景和实现方式,充分发挥 Redis 的优势。