水利局网站建设整改报告,网站设计 导航条,wordpress修改管理密码错误,网站建设规划ppt模板缓存预热缓存雪崩缓存击穿缓存穿透
● 缓存预热、雪崩、穿透、击穿分别是什么#xff1f;你遇到过那几个情况#xff1f; ● 缓存预热你是怎么做到的#xff1f; ● 如何避免或者减少缓存雪崩#xff1f; ● 穿透和击穿有什么区别#xff1f;它两一个意思还是截然不同缓存雪崩缓存击穿缓存穿透
● 缓存预热、雪崩、穿透、击穿分别是什么你遇到过那几个情况 ● 缓存预热你是怎么做到的 ● 如何避免或者减少缓存雪崩 ● 穿透和击穿有什么区别它两一个意思还是截然不同 ● 穿透和击穿你有什么解决方案如何避免 ● 加入出现了缓存不一致你有哪些修补方案
1、缓存预热 2、缓存雪崩 发生原因 ● Redis主机挂了Redis全盘崩溃偏硬件运维。 ● Redis中有大量key同时过期大面积失效偏软件开发。 预防解决 ● Redis中key设置为永不过期or过期时间错开 ● Redis缓存集群实现高可用 ○ 主从哨兵 ○ Redis Cluster ○ 开启Redis持久化机制RDB/AOF尽快恢复缓存集群 ● 多缓存结合预防雪崩 ○ ehcache本地缓存redis缓存 ● 服务降级 ○ Hystrix或者案例sentinel限流降级
3、缓存穿透 发生原因 请求去查一条记录先查Redis无后查MySQL无都查不到该条记录但是请求每次都会打到数据库上面去导致后台数据库压力暴增这种就是缓存穿透。 简单来说就是本来无一物两库都没有既不在Redis缓存库也不再MySQL数据库存在被多次暴击风险
解决方案
方案一空对象缓存或者缺省值 一般正常情况下使用回写增强mysql也查不到的话就让redis存入刚刚查不到的key并保护mysql第一次来查询没有查询到redis和mysql都没有返回null给调用者但是增强回写后第二次查同样的key此时redis就有值了可以直接从redis中读取default缺省值返回给业务程序避免了把大量请求发送给mysql处理打爆mysql------------此种方法架不住黑客的恶意攻击有缺陷…只能解决key相同的情况。 黑客或者恶意攻击黑客会对你的系统进行攻击拿一个不存在的id去查询数据会产生大量的请求到数据库查询可能会导致你的数据库由于压力过大而宕机。 key相同—第一次达到mysql空对象缓存后第二次就返回default缺省值避免mysql再被攻击不用再到数据库中走一圈了。 key不同—由于存在空对象缓存和缓存回写看自己的业务redis中无关紧张的key也会越来越多记得设置redis过期时间。
方案二Google布隆过滤器Guava解决缓存穿透 Guava中布隆过滤器的实现算是比较权威的所以实际项目中可以直接采用Guava布隆过滤器 白名单过滤器实战 白名单那过滤器架构说明 误判问题概率小还可以接受不能从布隆过滤器中删除 全部合法的key都需要放入Guava版布隆过滤器Redis里面不然数据就是返回null 改POM !-- Guava Google开源的Guava中自带的布隆过滤器--dependencygroupIdcom.google.guava/groupIdartifactIdguava/artifactIdversion23.0/version/dependency业务类 我们的目的是再白名单里面设置100w的数据然后再额外加入10w的数据看一下误判率是多少
/*** author Guanghao Wei* create 2023-04-25 14:51*/
Service
Slf4j
public class GuavaWithBloomFilterService {//定义常量public static final int _1W 10000;//定义guava布隆过滤器初始容量public static final int SIZE 100 * _1W;//误判率它越小误判个数越少public static double fpp 0.03;//创建guava布隆过滤器private BloomFilterInteger bloomFilter BloomFilter.create(Funnels.integerFunnel(), SIZE, fpp);public void guavaBloomFilter() {//先让bloomFilter加入100w数据for (int i 1; i SIZE; i) {bloomFilter.put(i);}//故意取10w个不在合法范围内的数据ArrayListObject list new ArrayList(10 * _1W);//验证for (int i SIZE 1; i SIZE (10 * _1W); i) {if (bloomFilter.mightContain(i)) {log.info(被误判了{}, i);list.add(i);}}log.info(误判总数量{}, list.size());}
}/*** author Guanghao Wei* create 2023-04-25 14:51*/
Api(tags google工具Guava处理布隆过滤器)
RestController
Slf4j
public class GuavaWithBloomFilterController {Autowiredprivate GuavaWithBloomFilterService guavaWithBloomFilterService;ApiOperation(guava布隆过滤器插入100万样本数据并额外添加10w测试是否存在)GetMapping(guavafilter)public void guavaBloomFilter() {guavaWithBloomFilterService.guavaBloomFilter();}
}
这里有一个误判率的知识点我们通过debug源码来学习
布隆过滤器说明
缓存击穿 是什么 大量的请求同时查询一个key时此时这个key正好失效了就会导致大量的请求都打到数据库上去 简单来说就是热点key突然失效了暴打mysql。
穿透和击穿截然不同 热点key为什么失效
时间到了自然清除但还未被访问到 delete掉的key刚巧又被访问
危害 会造成某一时刻数据库请求量过大压力剧增 一般技术部门需要知道热点key是哪些做到心里有数防止击穿
解决 方案一:差异失效时间 对于访问频繁的热点key干脆就不设置过期时间
方案二互斥更新 采用双检加锁策略 案例
天猫聚划算功能实现防止缓存击穿 数据类型可以选用list和zset但这类场景一般还是选择list
实体类
/*** author Guanghao Wei* create 2023-04-25 15:40*/
Data
AllArgsConstructor
NoArgsConstructor
ApiModel(value 聚划算活动product信息)
public class Product {private Long id;private String name;private Integer price;private String detail;
}service
/*** author Guanghao Wei* create 2023-04-25 15:42*/
Service
Slf4j
public class JHSTaskService {public static final String JHS_KEY jhs;public static final String JHS_KEY_A jhs:a;public static final String JHS_KEY_B jhs:b;Autowiredprivate RedisTemplate redisTemplate;/*** 模拟从数据库读取20件特价商品用于加载到聚划算的页面中** return*/private ListProduct getProductsFromMysql() {ListProduct list new ArrayList();for (int i 1; i 20; i) {Random random new Random();int id random.nextInt(10000);Product obj new Product((long) id, product i, i, detail);list.add(obj);}return list;}PostConstructpublic void init() {log.info(启动定时器天猫聚划算功能模拟开始.........O(∩_∩)O);//用线程模拟定时任务后台任务定时将mysql里面的参加活动的商品刷进redisnew Thread(() - {//模拟从mysql查出数据用于加载进redis在页面展示ListProduct productList this.getProductsFromMysql();//采用redis list数据结构的lpush命令来存储redisTemplate.delete(JHS_KEY);//加入最新的数据redisTemplate.opsForList().leftPushAll(JHS_KEY, productList);//暂停1分钟间隔一分钟执行一次模拟聚划算一天执行的参加活动的品牌try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }}, t1).start();}
}
controller
/*** author Guanghao Wei* create 2023-04-25 15:42*/
Api(tags 聚划算页面展示控制器)
RestController
Slf4j
public class JHSProductController {public static final String JHS_KEY jhs;Autowiredprivate RedisTemplate redisTemplate;/*** 分页查询在高并发的情况下只能走Redis查询走db的话必定会吧db打垮** param page* param size* return*/ApiOperation(聚划算案例每次1页展示5条数据)GetMapping(product/find)public ListProduct find(int page, int size) {ListProduct list null;long start (page - 1) * size;long end start size - 1;try {list redisTemplate.opsForList().range(JHS_KEY, start, end);if (CollectionUtils.isEmpty(list)) {//走数据库查询 TODO}log.info(参加活动的商家{},list);} catch (Exception e) {//出异常了一般redis宕机了或者redis网络抖动导致timeoutlog.error(jhs exception:{},e);e.printStackTrace();//再次查询}return list;}}至此步骤上述聚划算的功能算是完成了请思考在高并发情况下又会产生什么样的经典生产问题 Bug和隐患说明
热点key突然失效导致可怕的缓存击穿delete命令执行的一瞬间有空隙其他请求线程找Redis为null达到mysql暴击mysql… 复习again
最终目的2条命令原子性是其次的主要是防止热点key突然失效暴击mysql打爆系统。 进一步升级加固案例 互斥更新—双检加锁策略
差异失效时间在本案例中给我们使用这个方式 PostConstructpublic void initJHSAB() {log.info(启动AB定时器天猫聚划算功能模拟开始.........O(∩_∩)O DateUtil.now());//用线程模拟定时任务后台任务定时将mysql里面的参加活动的商品刷进redisnew Thread(() - {//模拟从mysql查出数据用于加载进redis在页面展示ListProduct productList this.getProductsFromMysql();//先更新B缓存且让B过期时间超过AB做兜底redisTemplate.delete(JHS_KEY_B);redisTemplate.opsForList().leftPushAll(JHS_KEY_B, productList);redisTemplate.expire(JHS_KEY_B, 86410L, TimeUnit.SECONDS);//在更新A缓存redisTemplate.delete(JHS_KEY_A);redisTemplate.opsForList().leftPushAll(JHS_KEY_A, productList);redisTemplate.expire(JHS_KEY_A, 86400L, TimeUnit.SECONDS);//暂停1分钟间隔一分钟执行一次模拟聚划算一天执行的参加活动的品牌try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }}, t1).start();}ApiOperation(AB双缓存架构防止热点key突然失效)GetMapping(product/findAB)public ListProduct findAB(int page, int size) {ListProduct list null;long start (page - 1) * size;long end start size - 1;try {list redisTemplate.opsForList().range(JHS_KEY_A, start, end);if (CollectionUtils.isEmpty(list)) {log.info(-------A缓存已经失效或者过期了记得人工修改B缓存继续顶着);list redisTemplate.opsForList().range(JHS_KEY_B, start, end);if (CollectionUtils.isEmpty(list)) {//TODO 走数据库查询}}} catch (Exception e) {//出异常了一般redis宕机了或者redis网络抖动导致timeoutlog.error(jhs exception:{}, e);e.printStackTrace();//再次查询}return list;}