1. Redis 缓存失效与分布式锁实战经验分享
作为一名后端开发者,Redis 是我们日常工作中不可或缺的利器。但在实际应用中,我遇到过不少令人头疼的问题。今天我想分享几个典型的 Redis 使用场景中的坑点,以及我是如何解决这些问题的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 缓存雪崩问题与解决方案
2.1 问题现象与原因分析
记得有一次凌晨三点,监控系统突然报警,服务响应时间从正常的50ms飙升到5000ms,数据库CPU直接打满。紧急排查后发现,原来是我们给所有缓存都设置了相同的2小时过期时间,导致凌晨2点整大批缓存同时失效,所有请求直接打到数据库。
java复制// 问题代码示例
public User getUserById(Long id) {
String cacheKey = "user:" + id;
String cached = redis.get(cacheKey);
if (cached != null) {
return JSON.parseObject(cached, User.class);
}
User user = userMapper.selectById(id);
redis.setex(cacheKey, 7200, JSON.toJSONString(user));
return user;
}
这段代码的问题在于:
- 所有缓存设置了相同的过期时间(7200秒)
- 没有考虑缓存失效后的降级方案
- 系统启动时没有进行缓存预热
2.2 解决方案与优化实践
2.2.1 随机过期时间+逻辑过期
java复制public User getUserById(Long id) {
String cacheKey = "user:" + id;
String cached = redis.get(cacheKey);
if (cached != null) {
User user = JSON.parseObject(cached, User.class);
if (isStale(user)) {
threadPool.execute(() -> refreshCache(id));
}
return user;
}
User user = userMapper.selectById(id);
int expireSeconds = 7200 + new Random().nextInt(3600);
redis.setex(cacheKey, expireSeconds, JSON.toJSONString(user));
return user;
}
2.2.2 缓存预热机制
java复制@Configuration
public class CachePreheatConfig implements CommandLineRunner {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private UserMapper userMapper;
@Override
public void run(String... args) {
List<User> hotUsers = userMapper.selectHotUsers();
for (User user : hotUsers) {
String cacheKey = "user:" + user.getId();
redisTemplate.opsForValue().set(cacheKey, user, 24, TimeUnit.HOURS);
}
}
}
2.2.3 服务降级策略
java复制public User getUserByIdWithLock(Long id) {
String cacheKey = "user:" + id;
String cached = redis.get(cacheKey);
if (cached != null) {
return JSON.parseObject(cached, User.class);
}
String lockKey = "lock:user:" + id;
Boolean acquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(acquired)) {
try {
cached = redis.get(cacheKey);
if (cached != null) {
return JSON.parseObject(cached, User.class);
}
User user = userMapper.selectById(id);
redis.setex(cacheKey, 7200, JSON.toJSONString(user));
return user;
} finally {
redis.delete(lockKey);
}
