当前位置: 首页> 科技> 互联网 > 长春火车站到龙嘉机场动车时刻表_武汉最近新闻大事_企业做网上推广_seo优化易下拉霸屏

长春火车站到龙嘉机场动车时刻表_武汉最近新闻大事_企业做网上推广_seo优化易下拉霸屏

时间:2025/7/15 14:05:56来源:https://blog.csdn.net/qq_26641137/article/details/147102297 浏览次数:0次
长春火车站到龙嘉机场动车时刻表_武汉最近新闻大事_企业做网上推广_seo优化易下拉霸屏

面试切入点
在这里插入图片描述

在这里插入图片描述
缓存预热
在这里插入图片描述
什么是预热?
mysql假如新增100条记录,一般默认以mysql为准作为底单数据,如何同步到redis(布隆过滤器),这100条合法数据??
为什么需要预热?
mysql有100条新记录,redis无
1.比较懒,什么都不做,只对mysql做了数据新增,利用redis的回写机制,让他逐步实现100条新增记录的同步。最好提前比如晚上部署发布版本的时候,由自己人提前做一次,让redis同步了,不要把这个问题留给客户。
2.通过中间件或者程序自行完成@PostConstruct。
缓存雪崩
发生

  • redis主机挂了,Redis全盘崩溃,偏硬件运维
  • redis中有大量key同时过期,大面积失效,偏软件开发

预防+解决

  • redis中key设置为永不过期或者过期时间错开

  • redis缓存集群实现高可用(主从+哨兵、Redis Cluster、开启Redis持久化机制aof或者rdb,尽快恢复缓存数据)

  • 多缓存结合预防雪崩(ehcache本地缓存+redis缓存)

  • 服务降级(Hystrix或者阿里sentinel限流或降级)
    在这里插入图片描述
    在这里插入图片描述

  • 人民币玩家 (阿里云-云数据库Redis版)

缓存穿透
是什么?
请求去查询一条记录,先看redis无,后查mysql无,都查询不到该条记录,但是请求每次都会打到数据库上面去,导致后台数据库的压力暴增,这种现象我们称为缓存穿透,这个redis变成了摆设。
简单来说,本来无一物,两库都没有。既不在Redis缓存库,也不在mysql,数据库存在被多次暴击风险。

解决
在这里插入图片描述
在这里插入图片描述方案1:空对象缓存或者缺省值

  • 一般情况OK
    在这里插入图片描述

  • 黑客攻击

    黑客会对你的系统进行攻击,拿一个不存在的id去查询数据,会产生大量的请求到数据库去查询。可能会导致你的数据库由于压力过大而宕掉。
    key相同打你系统
    第一次打到mysql,空对象缓存后第二次就返回defaultNull缺省值,避免mysql被攻击,不用再到数据库中去走一圈了
    key不同打你系统
    由于存在空对象缓存和缓存回写(看自己业务不限死),redis中的无关紧要的key也会越写越多(记得设置redis过期时间)

方案2:Google布隆过滤器Guava解决缓存穿透
Guava 中布隆过滤器的实现算是比较权威的,所以实际项目中我们可以直接使用Guava布隆过滤器
案例:白名单过滤器
架构
在这里插入图片描述
误判问题,但是概率小可以接受,不能从布隆过滤器删除 。全部合法的key都需要放入Guava版布隆过滤器+redis里面,不然数据返回就是null

引入pom

 <!--guava Google 开源的 Guava 中自带的布隆过滤器--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.0</version></dependency>

入门使用

 /*** 测试布隆过滤器 demo*/@Testpublic void testGuavaBloomFilter(){//1.创建guava版本的布隆过滤器BloomFilter<Integer> integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), 100);//2.判断指定的元素是否存在System.out.println(integerBloomFilter.mightContain(1));System.out.println(integerBloomFilter.mightContain(2));System.out.println();//3.往过滤器加入元素integerBloomFilter.put(1);integerBloomFilter.put(2);System.out.println(integerBloomFilter.mightContain(1));System.out.println(integerBloomFilter.mightContain(2));}

运行效果
在这里插入图片描述
取样本数据100W,查查不在这个样本范围内的10W数据是否存在??

Controller层

import com.atguigu.redis7.service.GuavaBloomFilterService;
import com.atguigu.redis7.service.GuavaFilterService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@Api(tags = "google工具Guava处理布隆过滤器")
@RestController
@Slf4j
public class GuavaBloomFilterController
{@Resourceprivate GuavaFilterService guavaBloomFilterService;@ApiOperation("guava布隆过滤器插入100万样本数据并额外10W测试是否存在")@RequestMapping(value = "/guavafilter",method = RequestMethod.GET)public void guavaBloomFilter(){guavaBloomFilterService.guavaBloomFilter();}
}

service层

package com.atguigu.redis7.service;import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import java.util.ArrayList;@Service
@Slf4j
public class GuavaFilterService
{//1 定义一个常量public static final int _1W = 10000;//2 定义我们guava布隆过滤器,初始容量public static final int SIZE = 100 * _1W;//3 误判率,它越小误判的个数也就越少(思考,是否可以是无限小??没有误判岂不是更好)public static double fpp = 0.03;//0.01 0.000000000000001//4 创建guava布隆过滤器private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), SIZE,fpp);public void guavaBloomFilter(){//加入100W白名单的数据for (int i = 1; i <=SIZE ; i++) {bloomFilter.put(i);}//取10W个不在合法范围内的数据进行布隆过滤器的误判验证ArrayList<Integer> 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());}
}

实际效果
在这里插入图片描述
误判结果:3033
在这里插入图片描述
在这里插入图片描述
误判率的大小与坑位以及hash函数的关系:
0.03的误判率,hash函数是5,但是0.01时变成7,同时坑位数目变多。
在这里插入图片描述
默认误判率为0.03
在这里插入图片描述
布隆过滤器说明
在这里插入图片描述
实现黑名单机制
在这里插入图片描述
缓存击穿
是什么?
大量的请求同时查询一个key时,此时的这个key正好失效了,就会导致大量的请求都打到数据库上面去了。
简单说就是热点key突然失效了,暴打Mysql
备注:穿透和击穿,截然不同
在这里插入图片描述

危害

  • 会造成某一时刻数据库的请求量过大,压力剧增
  • 一般技术部门**需要知道热点key是那些个?**做到心里有数防止击穿
    解决
    在这里插入图片描述
    热点key失效
  • 时间到了自然清除但还被访问到
  • delete掉的key,刚巧又被访问
    解决方案
  • 方案1:差异失效时间,对于访问频繁的热点key,干脆就不设置过期时间
  • 方案2:互斥更新,采用双重校验加锁的策略
    在这里插入图片描述

案例:天猫聚划算功能实现+防止缓存击穿
在这里插入图片描述
问题:热点key突然失效了,导致缓存击穿
技术方案实现:
分析过程:
在这里插入图片描述
redis数据类型选型
在这里插入图片描述
业务类

package com.atguigu.redis7.entities;import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "聚划算活动producet信息")
public class Product
{//产品IDprivate Long id;//产品名称private String name;//产品价格private Integer price;//产品详情private String detail;
}

controller层

package com.atguigu.redis7.controller;import com.atguigu.redis7.entities.Product;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@Slf4j
@Api(tags = "聚划算商品列表接口")
public class JHSProductController
{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;/*** 分页查询:在高并发的情况下,只能走redis查询,走db的话必定会把db打垮* @param page* @param size* @return*/@RequestMapping(value = "/pruduct/find",method = RequestMethod.GET)@ApiOperation("聚划算案例,每次1页每页5条显示")public List<Product> find(int page, int size) {List<Product> list=null;long start = (page - 1) * size;long end   = start + size - 1;try{// 采用redis list结构里面的lrang命令来实现加载和分页查询list = redisTemplate.opsForList().range(JHS_KEY,start,end);if(CollectionUtils.isEmpty(list)){//TODO 走mysql查询}log.info("参加活动的商家:{}",list);}catch (Exception e){// 出异常了,一般redis宕机了或者redis网络抖动导致timeoutlog.error("jhs exception:{}",e);e.printStackTrace();// ....再次查询mysql}return list;}@RequestMapping(value = "/pruduct/findab",method = RequestMethod.GET)@ApiOperation("AB双缓存架构,防止热点key突然失效")public List<Product> findAB(int page, int size) {List<Product> 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 走mysql查询}}}catch (Exception e){// 出异常了,一般redis宕机了或者redis网络抖动导致timeoutlog.error("jhs exception:{}",e);e.printStackTrace();// ....再次查询mysql}return list;}}

采用定时器将参与聚划算的特价商品写入redis中


@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;/*** 偷个懒不加mybatis了,模拟从数据库读取20件特价商品,用于加载到聚划算的页面中* @return*/private List<Product> getProductsFromMysql() {List<Product> list=new ArrayList<>();for (int i = 1; i <=20; i++) {Random rand = new Random();int id= rand.nextInt(10000);Product obj=new Product((long) id,"product"+i,i,"detail");list.add(obj);}return list;}@PostConstructpublic void initJHS(){log.info("启动定时器天猫聚划算功能模拟开始......,O(∩_∩)O哈哈~");//1 用线程模拟定时任务,后台任务定时将mysql里面的参加活动的商品刷新到redis里new Thread(() -> {while (true){//2 模拟从mysql查出数据,用于加载到redis并给聚划算页面显示List<Product> list = this.getProductsFromMysql();//3 采用redis list数据结构的lpush命令来实现存储redisTemplate.delete(JHS_KEY);//4 加入最新的数据给redis参加活动redisTemplate.opsForList().leftPushAll(JHS_KEY,list);//5 暂停1分钟线程,间隔一分钟执行一次,模拟聚划算一天执行的参加活动的品牌try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }}},"t1").start();}

}

目前的效果
在这里插入图片描述
redis中的数据
在这里插入图片描述
请求接口
在这里插入图片描述
响应数据
在这里插入图片描述
基本功能完成,请思考一下在高并发的情境下有什么经典问题??
Bug和隐患说明

  • 热点key突然失效导致可怕的缓存击穿
    在这里插入图片描述
    delete命令执行的一瞬间有空隙,其他请求线程继续找Redis为null,打到了mysql,暴击。
    在这里插入图片描述
    最终目的:2条命令的原子性还是其次,主要是防止热key突然失效暴击mysql,打爆系统

互斥更新与双重校验登场
在这里插入图片描述
差异失效时间登场
在这里插入图片描述
代码实现
在这里插入图片描述

两块缓存,先更新缓存B,再更新缓存A

  @PostConstructpublic void initJHSAB(){log.info("启动AB定时器计划任务天猫聚划算功能模拟.........."+DateUtil.now());//1 用线程模拟定时任务,后台任务定时将mysql里面的参加活动的商品刷新到redis里new Thread(() -> {while (true){//2 模拟从mysql查出数据,用于加载到redis并给聚划算页面显示List<Product> list = this.getProductsFromMysql();//3 先更新B缓存且让B缓存过期时间超过A缓存,如果A突然失效了还有B兜底,防止击穿redisTemplate.delete(JHS_KEY_B);redisTemplate.opsForList().leftPushAll(JHS_KEY_B,list);redisTemplate.expire(JHS_KEY_B,86410L,TimeUnit.SECONDS);//4 再更新A缓存redisTemplate.delete(JHS_KEY_A);redisTemplate.opsForList().leftPushAll(JHS_KEY_A,list);redisTemplate.expire(JHS_KEY_A,86400L,TimeUnit.SECONDS);//5 暂停1分钟线程,间隔一分钟执行一次,模拟聚划算一天执行的参加活动的品牌try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }}},"t1").start();}

查询逻辑:先查询A,再查询B,缓存都没有,才去查询数据库

 @RequestMapping(value = "/pruduct/findab",method = RequestMethod.GET)@ApiOperation("AB双缓存架构,防止热点key突然失效")public List<Product> findAB(int page, int size) {List<Product> 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 走mysql查询}}}catch (Exception e){// 出异常了,一般redis宕机了或者redis网络抖动导致timeoutlog.error("jhs exception:{}",e);e.printStackTrace();// ....再次查询mysql}return list;}

实现效果
在这里插入图片描述
接口调用
在这里插入图片描述
在这里插入图片描述
A失效了
在这里插入图片描述
B兜底
在这里插入图片描述
在这里插入图片描述
总结
在这里插入图片描述

视频链接
Redis缓存之预热、击穿、穿透、雪崩

关键字:长春火车站到龙嘉机场动车时刻表_武汉最近新闻大事_企业做网上推广_seo优化易下拉霸屏

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: