在 Java 项目中,Redis 因其高性能、丰富的数据结构和原子操作,已经成为不可或缺的组件。以下是 Redis 最常用、最经典的业务场景,并结合 Java 代码示例进行说明。
一、缓存(Cache) - 最核心的场景
目的:减轻数据库压力,加速读写,提升系统吞吐量。
- 原理:将数据库查询结果等耗时计算的结果缓存起来。下次请求时,先查缓存,命中则直接返回;未命中再查数据库,并回填缓存。
- Java 实现:
- 注解驱动:使用 Spring Cache 抽象,搭配
@Cacheable,@CacheEvict,@CachePut等注解,无侵入式实现缓存。 - 手动操作:直接使用
RedisTemplate或Jedis/Lettuce客户端进行更灵活的控制。
- 注解驱动:使用 Spring Cache 抽象,搭配
// 1. 使用 Spring Cache 注解 (最简单)
@Service
public class ProductService {
@Cacheable(value = "product", key = "#id") // 缓存名为product,key是id
public Product getProductById(Long id) {
// 模拟查询数据库
return productRepository.findById(id).orElse(null);
}
@CacheEvict(value = "product", key = "#id") // 更新或删除商品时,清除缓存
public void updateProduct(Product product) {
productRepository.save(product);
}
}
// 2. 手动使用 RedisTemplate (更灵活,例如设置TTL)
@Component
public class ProductServiceWithTemplate {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public Product getProductById(Long id) {
String key = "product:" + id;
// 1. 先查缓存
Product product = (Product) redisTemplate.opsForValue().get(key);
if (product != null) {
return product;
}
// 2. 缓存未命中,查数据库
product = productRepository.findById(id).orElse(null);
if (product != null) {
// 3. 回填缓存,并设置30分钟过期
redisTemplate.opsForValue().set(key, product, Duration.ofMinutes(30));
}
return product;
}
}
注意事项:缓存穿透、缓存击穿、缓存雪崩、数据一致性。
二、分布式锁(Distributed Lock)
目的:在分布式系统中,确保多个服务实例对共享资源的互斥访问。
- 原理:利用 Redis 的
SETNX(SET if Not eXists)命令或SET命令的NX、PX选项来争抢一个唯一的 Key。抢到即获得锁。 - Java 实现:
- 手动实现:使用
setIfAbsent方法。 - 框架:使用 Redisson 库,它提供了成熟的分布式锁实现。
- 手动实现:使用
// 1. 使用 RedisTemplate 手动实现(简易版)
public boolean tryLock(String lockKey, String requestId, long expireTime) {
return redisTemplate.opsForValue().setIfAbsent(
lockKey,
requestId,
Duration.ofMillis(expireTime)
);
}
public void unlock(String lockKey, String requestId) {
// 关键:使用Lua脚本保证判断和删除是原子的
String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript<Long> script = RedisScript.of(luaScript, Long.class);
Long result = redisTemplate.execute(script, Collections.singletonList(lockKey), requestId);
// 根据result判断是否解锁成功
}
// 2. 使用 Redisson(推荐,功能完善)
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
// 在业务代码中
public void doSomethingWithLock() {
RLock lock = redissonClient.getLock("myLock");
try {
// 尝试加锁,最多等待100秒,上锁后30秒自动解锁
if (lock.tryLock(100, 30, TimeUnit.SECONDS)) {
try {
// 执行业务代码
} finally {
lock.unlock(); // 确保最终解锁
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
三、会话存储(Session Store)
目的:实现分布式会话,使服务端无状态化,便于水平扩展。
- 原理:将 Tomcat 等 Web 容器默认的内存 Session 存储,替换为集中式的 Redis 存储。所有服务实例都从 Redis 读写 Session 数据。
- Java 实现:使用
spring-session-data-redis库,配置简单,几乎零代码入侵。
<!-- pom.xml 添加依赖 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
# application.yml
spring:
session:
store-type: redis
redis:
host: localhost
port: 6379
// 之后,使用 HttpSession 就和原来完全一样,但数据存在Redis中
@RestController
public class SessionController {
@PostMapping("/login")
public String login(HttpSession session, @RequestBody User user) {
// session.setAttribute 会自动存入Redis
session.setAttribute("user", user);
return "success";
}
@GetMapping("/user")
public User getUser(HttpSession session) {
// session.getAttribute 会自动从Redis读取
return (User) session.getAttribute("user");
}
}
四、计数器(Counter)与限流(Rate Limiting)
目的:实现点赞、浏览数统计、接口访问频率限制等功能。
- 原理:利用 Redis 的
INCR、INCRBY命令实现原子性计数。利用EXPIRE命令实现时间窗口。 - Java 实现:
@Service
public class CounterService {
@Autowired
private RedisTemplate<String, Integer> redisTemplate; // 可使用StringRedisTemplate
// 文章点赞数增加
public Long incrementArticleLike(Long articleId) {
String key = "article:like:" + articleId;
return redisTemplate.opsForValue().increment(key);
}
// 获取文章点赞数
public Integer getArticleLikeCount(Long articleId) {
String key = "article:like:" + articleId;
return redisTemplate.opsForValue().get(key);
}
// 简单限流:1分钟内同一个ip只能访问10次
public boolean isAllowed(String ip) {
String key = "rate:limit:" + ip;
Long count = redisTemplate.opsForValue().increment(key);
if (count != null && count == 1) {
// 如果是第一次访问,设置key的过期时间为1分钟
redisTemplate.expire(key, Duration.ofMinutes(1));
}
return count != null && count <= 10;
}
}
五、排行榜(Leaderboard)
目的:实现实时更新的排行榜,如游戏积分榜、销量榜。
- 原理:使用 Redis 的 ZSet(有序集合)数据结构。
member是用户ID,score是分数。ZSet 会自动按分数排序。 - Java 实现:
@Service
public class RankingService {
@Autowired
private RedisTemplate<String, String> redisTemplate; // 通常用StringRedisTemplate
private static final String RANKING_KEY = "global:ranking";
// 更新用户分数
public void addScore(String userId, double score) {
redisTemplate.opsForZSet().add(RANKING_KEY, userId, score);
}
// 获取前10名
public Set<ZSetOperations.TypedTuple<String>> getTop10() {
return redisTemplate.opsForZSet().reverseRangeWithScores(RANKING_KEY, 0, 9);
}
// 获取用户排名
public Long getUserRank(String userId) {
// 排名从0开始,所以+1
return redisTemplate.opsForZSet().reverseRank(RANKING_KEY, userId) + 1;
}
}
六、消息队列(Pub/Sub)与延迟队列
目的:实现简单的服务间通知、事件发布/订阅、延迟任务。
- 原理:
- Pub/Sub:使用 Redis 的
publish/subscribe命令。发布者向频道发送消息,所有订阅该频道的消费者都会收到。注意:消息是即时的,无持久化。 - 延迟队列:使用 ZSet,将任务作为
member,执行时间戳作为score。后台线程轮询 ZSet 中score小于当前时间的任务来执行。
- Pub/Sub:使用 Redis 的
// 1. Pub/Sub 发布者
@Component
public class MessagePublisher {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void publish(String channel, Object message) {
redisTemplate.convertAndSend(channel, message);
}
}
// 2. Pub/Sub 订阅者(监听器)
@Component
public class MessageSubscriber implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
String channel = new String(message.getChannel());
String body = new String(message.getBody());
System.out.println("Received message: " + body + " from channel: " + channel);
// 处理业务逻辑
}
}
// 需要配置监听容器(在@Configuration类中)
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic("news.*")); // 订阅news开头的频道
return container;
}
总结
| 场景 | 核心数据结构 | 关键命令/特性 | Java 常用库 |
|---|---|---|---|
| 缓存 | String, Hash | SET, GET, SETEX | Spring Cache, RedisTemplate |
| 分布式锁 | String | SET NX PX | RedisTemplate, Redisson |
| 会话存储 | Hash | HSET, HGET | spring-session-data-redis |
| 计数器/限流 | String | INCR, EXPIRE | RedisTemplate |
| 排行榜 | ZSet | ZADD, ZREVRANGE | RedisTemplate |
| 消息队列 | - | PUBLISH, SUBSCRIBE | RedisTemplate |
选择建议:对于简单场景,优先使用 Spring Data Redis 的 RedisTemplate。对于复杂的分布式锁、分布式集合等需求,强烈推荐使用 Redisson,它封装了更多分布式功能,更加可靠和易用。
1145

被折叠的 条评论
为什么被折叠?



