文章目录

  • ①. 缓存雪崩
  • ②. 缓存穿透
  • ③. 在centos7下布隆过滤器2种安装方式
  • ④. 缓存击穿
  • ⑤. 高并发的淘宝聚划算案例落地

①. 缓存雪崩

  • ①. 问题的产生:缓存雪崩是指缓存数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库
    (redis主机挂了,Redis全盘崩溃、比如缓存中有大量数据同时过期)

  • ②. 解决方案

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
  2. 设置热点数据永远不过期
  3. 使用缓存预热
  • ③. 什么是缓存预热?
    缓存预热就是将数据提前加入到缓存中,当数据发生变更,再将最新的数据更新到缓存

②. 缓存穿透

  • ①. 请求去查询一条记录,先redis后mysql发现都查询不到该条记录,但是请求每次都会打到数据库上面去,导致后台数据库压力暴增,这种现象我们称为缓存穿透,这个redis变成了一个摆设(redis中没有、mysql也没有)

  • ②. 解决方案一:空对象缓存或者缺省值

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

  • ③. 解决方案二:Google布隆过滤器Guava解决缓存穿透(核心代码如下) 单机版
 //入门案列@Testpublic void bloomFilter(){// 创建布隆过滤器对象BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 100);// 判断指定元素是否存在System.out.println(filter.mightContain(1));System.out.println(filter.mightContain(2));// 将元素添加进布隆过滤器filter.put(1);filter.put(2);System.out.println(filter.mightContain(1));System.out.println(filter.mightContain(2));}
//取样本100W数据,查查不在100W范围内的其它10W数据是否存在
public class BloomfilterDemo {public static final int _1W = 10000;//布隆过滤器里预计要插入多少数据public static int size = 100 * _1W;//误判率,它越小误判的个数也就越少(思考,是不是可以设置的无限小,没有误判岂不更好)public static double fpp = 0.03;// 构建布隆过滤器//private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size);//不写误判率就用默认的0.03private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size);public static void main(String[] args) {//1 先往布隆过滤器里面插入100万的样本数据for (int i = 0; i < size; i++) {bloomFilter.put(i);}//故意取10万个不在过滤器里的值,看看有多少个会被认为在过滤器里List<Integer> list = new ArrayList<>(10 * _1W);for (int i = size+1; i < size + 100000; i++) {if (bloomFilter.mightContain(i)) {System.out.println(i+"\t"+"被误判了.");list.add(i);}}System.out.println("误判的数量:" + list.size());}
}


  • ④. 解决方案三:Redis布隆过滤器解决缓存穿透
    Guava 提供的布隆过滤器的实现还是很不错的 (想要详细了解的可以看一下它的源码实现),但是它有一个重大的缺陷就是只能单机使用 ,而现在互联网一般都是分布式的场景。为了解决这个问题,我们就需要用到Redis中的布隆过滤器了

  • ⑤. 针对方案三具体的案列说明:白名单过滤器

  1. 误判问题,但是概率小可以接受,不能从布隆过滤器删除
  2. 全部合法的key都需要放入过滤器+redis里面,不然数据就是返回null
public class RedissonBloomFilterDemo2 {public static final int _1W = 10000;//布隆过滤器里预计要插入多少数据public static int size = 100 * _1W;//误判率,它越小误判的个数也就越少public static double fpp = 0.03;static RedissonClient redissonClient = null;static RBloomFilter rBloomFilter = null;static {Config config = new Config();config.useSingleServer().setAddress("redis://192.168.68.143:6379").setDatabase(0);//构造redissonredissonClient = Redisson.create(config);//通过redisson构造rBloomFilterrBloomFilter = redissonClient.getBloomFilter("phoneListBloomFilter",new StringCodec());rBloomFilter.tryInit(size,fpp);// 1测试  布隆过滤器有+redis有rBloomFilter.add("10086");redissonClient.getBucket("10086",new StringCodec()).set("chinamobile10086");// 2测试  布隆过滤器有+redis无rBloomFilter.add("10087");//3 测试 ,都没有}public static void main(String[] args) {String phoneListById = getPhoneListById("10086");//String phoneListById = getPhoneListById("10086");//String phoneListById = getPhoneListById("10086");System.out.println("------查询出来的结果: "+phoneListById);//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }redissonClient.shutdown();}private static String getPhoneListById(String IDNumber) {String result = null;if (IDNumber == null) {return null;}//1 先去布隆过滤器里面查询if (rBloomFilter.contains(IDNumber)) {//2 布隆过滤器里有,再去redis里面查询RBucket<String> rBucket = redissonClient.getBucket(IDNumber, new StringCodec());result = rBucket.get();if(result != null) {return "i come from redis: "+result;}else{result = getPhoneListByMySQL(IDNumber);if (result == null) {return null;}// 重新将数据更新回redisredissonClient.getBucket(IDNumber,new StringCodec()).set(result);}return "i come from mysql: "+result;}return result;}private static String getPhoneListByMySQL(String IDNumber) {return "chinamobile"+IDNumber;}
}
  • ⑥. 公司具体案列

③. 在centos7下布隆过滤器2种安装方式

  • ①. 方式一:采用docker安装RedisBloom,推荐(具体步骤)
  1. Redis在4.0 之后有了插件功能(Module),可以使用外部的扩展功能,可以使用RedisBloom作为Redis布隆过滤器插件
  2. docker run -p 6379:6379 --name=redis6379bloom -d redislabs/rebloom
  3. docker exec -it redis6379bloom /bin/bash
  4. redis-cli
[root@TANG2021 ~]# docker exec -it myredistz /bin/bash
root@459c7c8679b1:/data# redis-cli
127.0.0.1:6379> keys *
1) "10086"
2) "{phoneListBloomFilter}:config"
3) "phoneListBloomFilter"
127.0.0.1:6379> type {phoneListBloomFilter}:config
hash
127.0.0.1:6379> hgetall {phoneListBloomFilter}:config
1) "size"
2) "7298440"
3) "hashIterations"
4) "5"
5) "expectedInsertions"
6) "1000000"
7) "falseProbability"
8) "0.03"
127.0.0.1:6379>
  • ②. 布隆过滤器常用操作命令
    bf.reserve filter 0.01 100 (默认、100个元素里的误判率是0.01)
操作 命令
添加值 bf.add key 值
查询值 bf.exists key 值
批量添加 bf.madd
批量查询 bf.mexists

  • ③. 方式二:编译安装
# 下载 编译 安装Rebloom插件
wget https://github.com/RedisLabsModules/rebloom/archive/v2.2.2.tar.gz
# 解压
tar -zxvf v2.2.2.tar.gz
cd RedisBloom-2.2.2
# 若是第一次使用 需要安装gcc++环境
make
# redis服启动添加对应参数 这样写还是挺麻烦的
# rebloom_module="/usr/local/rebloom/rebloom.so"
# daemon --user ${REDIS_USER-redis} "$exec $REDIS_CONFIG --loadmodule # $rebloom_module --daemonize yes --pidfile $pidfile"
# 记录当前位置
pwd
# 进入reids目录 配置在redis.conf中 更加方便
vim redis.conf
# :/loadmodule redisbloom.so是刚才具体的pwd位置 cv一下
loadmodule /xxx/redis/redis-5.0.8/RedisBloom-2.2.2/redisbloom.so
# 保存退出
wq
# 重新启动redis-server 我是在redis中 操作的 若不在请写出具体位置 不然会报错
redis-server redis.conf
# 连接容器中的 redis 服务 若是无密码 redis-cli即可
redis-cli -a 密码
# 进入可以使用BF.ADD命令算成功

④. 缓存击穿

  • ①. 缓存击穿:大量的请求同时查询一个key时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去(简单说就是热点key突然失效了,暴打mysql)

  • ②. 危害:会造成某一时刻数据库请求量过大,压力剧增

  • ③. 方案一:互斥更新、随机退避、差异失效时间

  • ④. 方案2:对于访问频繁的热点key,干脆就不设置过期时间

  • ⑤. 方案3:互斥独占锁防止击穿

public User findUserById2(Integer id) {User user = null;String key = CACHE_KEY_USER+id;//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysqluser = (User) redisTemplate.opsForValue().get(key);if(user == null) {//2 大厂用,对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysqlsynchronized (UserService.class){user = (User) redisTemplate.opsForValue().get(key);//3 二次查redis还是null,可以去查mysql了(mysql默认有数据)if (user == null) {//4 查询mysql拿数据user = userMapper.selectByPrimaryKey(id);//mysql有数据默认if (user == null) {return null;}else{//5 mysql里面有数据的,需要回写redis,完成数据一致性的同步工作//setnx 不存在才创建redisTemplate.opsForValue().setIfAbsent(key,user,7L,TimeUnit.DAYS);}}}}return user;
}

⑤. 高并发的淘宝聚划算案例落地

  • ①. 什么是淘宝聚划算?

  • ②. 分析过程

步骤 说明
1 100%高并发,绝对不可以用mysql实现
2. 先把mysql里面参加活动的数据抽取进redis,一般采用定时器扫描来决定上线活动还是下线取消
3 支持分页功能,一页20条记录
  • ③. redis数据类型选型
  • ④. springboot+redis实现高并发的淘宝聚划算业务
@Data
@ApiModel(value = "聚划算活动producet信息")
public class Product {private Long id;/*** 产品名称*/private String name;/*** 产品价格*/private Integer price;/*** 产品详情*/private String detail;public Product() {}public Product(Long id, String name, Integer price, String detail) {this.id = id;this.name = name;this.price = price;this.detail = detail;}
}
public class Constants {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";
}
@Service
@Slf4j
public class JHSTaskService{@Autowiredprivate RedisTemplate redisTemplate;@PostConstructpublic void initJHS(){log.info("启动定时器淘宝聚划算功能模拟.........."+DateUtil.now());new Thread(() -> {//模拟定时器,定时把数据库的特价商品,刷新到redis中while (true){//模拟从数据库读取100件特价商品,用于加载到聚划算的页面中List<Product> list=this.products();//采用redis list数据结构的lpush来实现存储this.redisTemplate.delete(Constants.JHS_KEY);//lpush命令this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY,list);//间隔一分钟 执行一遍try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }log.info("runJhs定时刷新..............");}},"t1").start();}/*** 模拟从数据库读取100件特价商品,用于加载到聚划算的页面中*/public List<Product> products() {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;}
}
@RestController
@Slf4j
@Api(description = "聚划算商品列表接口")
public class JHSProductController{@Autowiredprivate RedisTemplate redisTemplate;/*** 分页查询:在高并发的情况下,只能走redis查询,走db的话必定会把db打垮* http://localhost:5555/swagger-ui.html#/jhs-product-controller/findUsingGET*/@RequestMapping(value = "/pruduct/find",method = RequestMethod.GET)@ApiOperation("按照分页和每页显示容量,点击查看")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数据结构的lrange命令实现分页查询list = this.redisTemplate.opsForList().range(Constants.JHS_KEY, start, end);if (CollectionUtils.isEmpty(list)) {//TODO 走DB查询}log.info("查询结果:{}", list);} catch (Exception ex) {//这里的异常,一般是redis瘫痪 ,或 redis网络timeoutlog.error("exception:", ex);//TODO 走DB查询}return list;}
}
  • ⑤. 上述存在哪些生产的问题?QPS上1000后导致可怕的缓存击穿
    (互斥更新、随机退避、差异失效时间)


  • ⑥. 互斥更新、随机退避、差异失效时间
@PostConstruct
public void initJHSAB(){log.info("启动AB定时器计划任务淘宝聚划算功能模拟.........."+DateUtil.now());new Thread(() -> {//模拟定时器,定时把数据库的特价商品,刷新到redis中while (true){//模拟从数据库读取100件特价商品,用于加载到聚划算的页面中List<Product> list=this.products();//先更新B缓存this.redisTemplate.delete(Constants.JHS_KEY_B);this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY_B,list);this.redisTemplate.expire(Constants.JHS_KEY_B,20L,TimeUnit.DAYS);//再更新A缓存this.redisTemplate.delete(Constants.JHS_KEY_A);this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY_A,list);this.redisTemplate.expire(Constants.JHS_KEY_A,15L,TimeUnit.DAYS);//间隔一分钟 执行一遍try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }log.info("runJhs定时刷新..............");}},"t1").start();
}
@RequestMapping(value = "/pruduct/findab",method = RequestMethod.GET)
@ApiOperation("按照分页和每页显示容量,点击查看AB")
public List<Product> findAB(int page, int size) {List<Product> list=null;long start = (page - 1) * size;long end = start + size - 1;try {//采用redis list数据结构的lrange命令实现分页查询list = this.redisTemplate.opsForList().range(Constants.JHS_KEY_A, start, end);if (CollectionUtils.isEmpty(list)) {log.info("=========A缓存已经失效了,记得人工修补,B缓存自动延续5天");//用户先查询缓存A(上面的代码),如果缓存A查询不到(例如,更新缓存的时候删除了),再查询缓存Bthis.redisTemplate.opsForList().range(Constants.JHS_KEY_B, start, end);}log.info("查询结果:{}", list);} catch (Exception ex) {//这里的异常,一般是redis瘫痪 ,或 redis网络timeoutlog.error("exception:", ex);//TODO 走DB查询}return list;
}

REDIS13_缓存雪崩、缓存穿透、基于布隆过滤器解决缓存穿透的问题、缓存击穿、基于缓存击穿工作实际案例相关推荐

  1. 解决Redis缓存穿透之布隆过滤器详解

    文章目录 1. 什么是Bloom Filter(布隆过滤器) 1.1 布隆过滤器优点 1.2 布隆过滤器缺点 1.3 布隆过滤器使用场景 1.4 布隆过滤器检索过程 1.5 布隆过滤器的算法描述 2. ...

  2. 布隆过滤器解决缓存穿透_缓冲穿透/缓存击穿/缓存雪崩等问题解决办法

    ----------------------[感谢小昭提供的图片] 1.缓存穿透 定义:请求查询数据库中压根就不存在的数据,每次请求直接打在DB上,这种查询查询不存在数据的现象称为缓存穿透 穿透带来的 ...

  3. Redis 预防缓存穿透“神器” — 布隆过滤器

    1. 布隆过滤器 1.1 概念 在架构设计时有一种最常见的设计被称为布隆过滤器,它可以有效减少缓存穿透的情况.其主旨是采用一个很长的二进制数组,通过一系列的 Hash 函数来确定该数据是否存在. 布隆 ...

  4. 使用BloomFilter布隆过滤器解决缓存击穿、垃圾邮件识别、集合判重

    Bloom Filter是一个占用空间很小.效率很高的随机数据结构,它由一个bit数组和一组Hash算法构成.可用于判断一个元素是否在一个集合中,查询效率很高(1-N,最优能逼近于1). 在很多场景下 ...

  5. 基于布隆过滤器实现敏感词识别和过滤

    在当前的网络环境下,敏感词过滤已经是各大网站的"标准配置",如果不想被大量的垃圾信息充斥,除了使用机器人识别.验证码等验证工具,还需要阻止含有敏感词内容的发布,否则可能面临关站等风 ...

  6. 5 Redis缓存穿透、击穿、雪崩、分布式锁、布隆过滤器

    1 Redis 应用问题解决 1.1 缓存穿透 1.1.1 问题描述 key 对应的数据在数据源并不存在,每次针对此 key 的请求从缓存获取不到,请求都会压到数据源(数据库),从而可能压垮数据源.比 ...

  7. 布隆过滤器避免redis缓存穿透

    缓存穿透及布隆过滤器 Redis的基于缓存,极大地提升了应用程序的性能和效率,特别是数据查询方面,但是也带来了一些问题,比如典型的 缓存穿透.缓存雪崩.缓存击穿. 本篇先讲缓存穿透及其解决办法. (1 ...

  8. 清空缓存的命令_布隆过滤器应用——解决Redis缓存穿透问题

    1. 布隆过滤器 简要介绍布隆过滤器的概念和特点,详细知识请参考几篇参考文献或其它文章. 1.1 概念 简单点说,布隆过滤器本质是一个位数组. 当一个元素加入过滤器时,使用多个hash函数对元素求值, ...

  9. 22-09-20 西安 谷粒商城(04)Redisson做分布式锁、布隆过滤器、AOP赋能、自定义注解做缓存管理、秒杀测试

    Redisson 1.Redisson做分布式锁  分布式锁主流的实现方案: 基于数据库实现分布式锁 基于缓存(Redis),性能最高 基于Zookeeper,可靠性最高 Redisson是一个在Re ...

最新文章

  1. 工具_SublimeText
  2. 陈春花:管理到底是什么?
  3. 这个“老婆生成器”说我有5个前妻...
  4. 爬虫 spider06——解析数据
  5. poi 读取excel
  6. linux oracle 11g ora-00845,Linux下安装Oracle11g , MEMORY_TARGET(AMM)小于/dev/shm处理(ORA-00845)...
  7. 华为软件机试测试题C语言,华为软件测试面试经验
  8. 斐波那契数的两种实现方式———1.递归实现,2迭代实现
  9. android平板安装win10,如何给平板电脑装win10系统?
  10. 普通文件与迅雷、旋风、快车、RayFile的下载地址转换算法
  11. Keras入门级MNIST手写数字识别超级详细教程
  12. cass坡度土方计算案例_cass7.0土方计算详解
  13. 商标网报错504_网站502与504错误分析
  14. 安防经济逐渐景气下行 安企是否能排除万难冲出阴霾?
  15. Docker从入门到干活,看这一篇够了!
  16. 深信服终端检测响应平台EDR-远程命令执行漏洞
  17. 从页面获取form表单提交的数据
  18. 针对python Gui初学者,一个极致简陋的gui,有空再补充
  19. unity shader 实现Ps的 19号笔刷厚涂阴影效果
  20. 高考填志愿—利用大数据填报

热门文章

  1. Webtop Html5 桌面App开发 -- 整合人人网登陆
  2. Windows获取电脑的硬盘属性(转速,类型(HDD,SSD))
  3. 搜索引擎蜘蛛爬虫 User Agent 一览(便于采集)
  4. 算法设计与分析基础第三版
  5. Javascript邮箱正则表达式
  6. 一键Ghost后,如何用DiskGenius恢复硬盘上原来的数据
  7. HTML5 canvas 计算文字宽度的方法(measureText)
  8. php 获得图片大小,js获取图片大小_php 或js获取图片大小
  9. 认识大牛 深度学习“三剑客”
  10. mysql计算字段文本区分度