外贸网站,安装网站模版视频教程,网站 建设文档,手机网站制作推荐30分钟自学教程#xff1a;Redis缓存击穿原理与解决方案
目标
理解缓存击穿的定义及核心原因。掌握互斥锁、逻辑过期时间等预防技术。能够通过代码实现高并发场景下的缓存保护。学会熔断降级、热点探测等应急方案。 教程内容
0~2分钟#xff1a;缓存击穿的定义与典型场景 …30分钟自学教程Redis缓存击穿原理与解决方案
目标
理解缓存击穿的定义及核心原因。掌握互斥锁、逻辑过期时间等预防技术。能够通过代码实现高并发场景下的缓存保护。学会熔断降级、热点探测等应急方案。 教程内容
0~2分钟缓存击穿的定义与典型场景
定义某个热点Key突然过期导致瞬时高并发请求直接穿透缓存访问数据库引发数据库压力激增。与雪崩、穿透的区别 雪崩大量Key同时失效。穿透查询不存在的数据。击穿单个热点Key失效引发并发问题。 典型场景 秒杀商品Key过期时数万用户同时刷新页面。新闻热点事件缓存到期大量用户请求涌入。 2~5分钟代码模拟击穿场景Java示例
// 未做并发控制的查询方法模拟击穿问题
public Product getProduct(String id) { String key product: id; Product product redisTemplate.opsForValue().get(key); if (product null) { // 假设此时有1000个并发请求同时进入此逻辑 product productService.loadFromDB(id); redisTemplate.opsForValue().set(key, product, 1, TimeUnit.HOURS); } return product;
} 问题复现
使用JMeter或Postman模拟100个并发请求访问该接口观察数据库查询次数是否为1次理想或100次实际击穿现象。 5~12分钟解决方案1——互斥锁分布式锁
核心思想缓存失效时仅允许一个线程重建数据其他线程阻塞等待或重试。代码实现Redisson分布式锁
public Product getProductWithLock(String id) { String key product: id; Product product redisTemplate.opsForValue().get(key); if (product null) { RLock lock redissonClient.getLock(product_lock: id); try { if (lock.tryLock(3, 10, TimeUnit.SECONDS)) { // 尝试获取锁最多等待3秒 // 双重检查其他线程可能已更新缓存 product redisTemplate.opsForValue().get(key); if (product null) { product productService.loadFromDB(id); redisTemplate.opsForValue().set(key, product, 1, TimeUnit.HOURS); } } else { Thread.sleep(50); // 未获取锁短暂等待后重试 return getProductWithLock(id); } } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } return product;
} 关键点 锁粒度需精确到单个Key避免全局锁性能问题。必须设置锁超时时间防止死锁。 12~20分钟解决方案2——逻辑过期时间
原理缓存永不过期但业务层判断数据是否需更新牺牲一定一致性。代码实现封装逻辑过期时间
// 数据包装类包含逻辑过期时间
public class ProductWrapper { private Product product; private long expireTime; // 逻辑过期时间戳 // 构造方法、Getter/Setter
} public Product getProductWithLogicExpire(String id) { String key product: id; ProductWrapper wrapper redisTemplate.opsForValue().get(key); if (wrapper null) { // 首次加载数据 Product product productService.loadFromDB(id); wrapper new ProductWrapper(product, System.currentTimeMillis() 3600 * 1000); redisTemplate.opsForValue().set(key, wrapper); return product; } else if (wrapper.getExpireTime() System.currentTimeMillis()) { // 逻辑过期异步更新缓存 CompletableFuture.runAsync(() - { Product newProduct productService.loadFromDB(id); redisTemplate.opsForValue().set(key, new ProductWrapper(newProduct, System.currentTimeMillis() 3600 * 1000) ); }); } return wrapper.getProduct(); // 返回旧数据保证可用性
} 适用场景 读多写少的热点数据如商品详情、新闻内容。允许短暂的数据不一致。 20~25分钟解决方案3——热点Key探测与自动续期
原理监控高频访问的Key提前续期或标记为永久有效。代码实现简单监控逻辑
// 热点Key监控器伪代码
public class HotKeyMonitor { private ConcurrentHashMapString, AtomicInteger accessCount new ConcurrentHashMap(); Scheduled(fixedRate 60000) // 每分钟扫描一次 public void scanHotKeys() { accessCount.forEach((key, count) - { if (count.get() 1000) { // 访问量阈值 redisTemplate.expire(key, 2, TimeUnit.HOURS); // 自动续期 count.set(0); // 重置计数器 } }); } public void recordAccess(String key) { accessCount.computeIfAbsent(key, k - new AtomicInteger(0)).incrementAndGet(); }
} // 在查询方法中记录访问
public Product getProduct(String id) { String key product: id; hotKeyMonitor.recordAccess(key); // ...其他逻辑
} 25~28分钟应急处理方案
熔断降级Sentinel示例
SentinelResource(value getProduct, blockHandler handleBlock)
public Product getProduct(String id) { // 正常业务逻辑...
} // 熔断后返回默认数据
public Product handleBlock(String id, BlockException ex) { return new Product(默认商品, 0.0);
} 缓存预热
// 定时预热即将过期的热点Key
Scheduled(cron 0 0/5 * * * ?) // 每5分钟执行一次
public void preloadHotData() { ListString hotKeys hotKeyMonitor.getHotKeys(); hotKeys.forEach(key - { Long ttl redisTemplate.getExpire(key); if (ttl ! null ttl 60) { // 剩余时间小于60秒 String id key.split(:)[1]; Product product productService.loadFromDB(id); redisTemplate.opsForValue().set(key, product, 1, TimeUnit.HOURS); } });
} 28~30分钟总结与优化方向
核心原则分散并发压力、异步更新、监控兜底。高级优化 结合Redis Cluster实现高可用。使用本地缓存如Caffeine作为一级缓存。 练习与拓展
练习
实现基于Redisson的分布式锁并通过JMeter验证并发控制效果。修改逻辑过期时间代码支持动态调整过期阈值如30分钟~2小时随机。
推荐拓展
研究RedLock算法及其在分布式锁中的应用。学习Redis的持久化机制RDB/AOF与数据恢复。探索开源框架JetCache对缓存击穿的自动化处理方案。