系统龟速运行,你以为加一个缓存就没事了?图样图森破!今天就说说redis作为缓存遇到的常见问题:缓存穿透、缓存击穿、缓存雪崩。

解决高并发问题的其中一项措施是使用缓存,而通常的技术选型就是redis。

用户访问网站时,为了避免每次都到持久层(如mysql)中获取数据,可以先到缓存(如Redis)中获取;如果缓存中获取不到,才到数据库中获取,同时将获取到的数据缓存到redis中。加缓存的目的是让用户尽可能少的访问数据库,尽可能多的访问缓存数据,从而提高网站的响应速度,保证网站的高并发,保护持久层数据的安全,同时提升用户的体验。

有个黑帽子,一直使用订单id=-1的请求参数访问你的网站,会怎么样?

缓存穿透

比如数据中不存在id=-1的订单数据,如果请求查询这条数据,则缓存中查不到,会将请求打到下层的数据库上,这就是缓存穿透;

查询缓存中不存在的数据会导致缓存穿透。需要注意的是,低频的缓存穿透是不可避免的,但是需要避免高频的缓存穿透。

如果有人恶意并发访问数据库中不存在数据,就可能会导致数据库因扛不住大的并发而引起系统瘫痪!

解决方案一:缓存空对象

请求到redis,当redis没有命中该数据时,请求会到达mysql;如果mysql也不存在该数据时,则缓存一个空对象到redis中。这样就可以解决缓存穿透问题。

 public String getOrderInfo(String orderId){// 查询缓存String orderInfoStr = redisClient.get(orderId);// 缓存不存在,查询数据库if(orderInfoStr == null){OrderInfo orderInfo = orderMapper.selectByOrderId(orderId)if(orderInfo != null){// 数据库存在,则正常缓存orderInfoStr = JSON.toJSONString(orderInfo);redisClient.set(orderId,orderInfoStr,2L * 60 * 60 * 1000,TimeUnit.MILLISECONDS);}else{// 数据库存在,则短时间缓存空值orderInfoStr = "";redisClient.set(orderId,orderInfoStr,30 * 60 * 1000,TimeUnit.MILLISECONDS);}    }return orderInfoStr;}

问题来了,当数据库中真的插入了该条数据,请求过来后只能拿到缓存中的空对象,该如何解决呢?

我们可以针对这种数据设置一个较短的过期时间,保证数据库和redis的弱一致性,就可以在一定程度上解决这个问题。

缓存空对象就没有其他问题了吗?no!!!

上面那个黑帽子使用订单id=-1的参数高并发请求你的系统,发现你的系统依然稳如狗,于是简单的改变了一下策略,随机生成负数订单id后高并发请求你的系统,你的系统会怎样?

这时,你的redis中会缓存大量的值为空的key,这会导致大量的内存占用,同时Redis有LRU或LFU的内存淘汰策略,可能会将缓存中有价值的数据淘汰掉,真正的用户请求过来会将请求打到数据库上。

所以,这种方式的缺点是:

  • 可能会缓存很多值为空的key,占用内存空间。同时Redis有LRU或LFU的内存淘汰策略,可能会将缓存中有价值的数据淘汰掉。
  • 对空值设置了时间,可能会导致数据库和redis中在某个时间段的数据不一致。

解决方案二:布隆过滤器

没有什么问题是加一层不能解决的!如果有,就再加一层! 为了防止缓存穿透,可以将数据库中存在的id提前存放在一个List数组,后续,请求到达controller层,可以到List数组中查询这个id是否存在,如果存在则将请求往下发送,如果不存在,则直接返回。

懂门路的朋友马上就看出破绽了,如果数据库中的数据量很大,这个List数据会占用很大的空间,同时查询的效率也会降低,有没有解决办法呢?当然有!使用Bloom Filter。

Bloom Filter是一个占用空间很小、效率很高的随机数据结构,它由一个bit数组和一组Hash算法构成。可用于判断一个元素是否在一个集合中,查询效率很高,内节省内存空间。

Bloom Filter在这里留个坑,以后有机会详细再说。

需要注意的是,Bloom Filter存在哈希碰撞问题,有一定的错误率;但是有一点可以肯定的是:Bloom Filter说这条数据存在,这条数据不一定存在;但是Bloom Filter说这条数据不存在,这条数据一定不存在。要使用好Bloom Filter过滤器,要从bit数组和Hash算法的角度做优化。

缓存击穿

缓存击穿是缓存穿透的特殊表现之一。一般的公司没有这样的业务,没有这样一条非常热的数据能够导致数据库崩溃,所以不需要解决。

当某个数据被高并发访问时,如果这个数据redis的key的突然失效,会导致这些请求同一时间打到数据库,数据库扛不住就会导致系统瘫痪。比如微博上突然爆出某个明星的出轨、结婚等消息,大家同时都到微博上搜这个明星的信息,这时如果这个热点key过期,就会导致微博挂掉。志玲姐姐结婚了,微博的程序猿已经够伤心的了,还要加班修复系统。。。

对于一般的公司来说,不会存在一条这样的热点数据,当这条数据失效的一瞬间将请求打到数据库从而导致系统崩溃的,所以也不用过度担心这个问题。

解决方案一:热点数据不过期

最简单的方式就是,缓存这些热点数据的时候,不设置过期时间,这样就不用担心这个问题了。

新的问题又来了,当数据库中这些已有的数据发生变化了,缓存没有变,导致数据不一致,怎么办?

这就需要一种方式来保证数据库和redis中的数据的一致性。数据库和redis的强一致是很难做到的,但是弱一致性还是可以保证的。方式有很多,比如数据库中数据发生变化时,可以同步更新redis中的数据;可以通过相关的中间件监控mysql的binlog日志并更新redis等。

解决方案二:分布式锁

当热点key过期,允许这个热点key在redis中查询不到数据时将其中一个请求打到达数据库,但不是并发打到数据库,然后将数据缓存到redis,后面的请求就可以到redis中查询到数据了。

比如100W个人同时到微博查看志林解决结婚的这条信息,这时正好redis中的这个热点key过期,假设微博后端部署了10台服务器,如何保证这100w人中只有一个人的请求在查询不到缓存数据将请求打到数据库,并将查询到的数据缓存到redis,然后其他999999个请求都从缓存中拿数据呢?

显然JDK提供的java同步锁synchronized是不能实现的,因为这种方式只能在一个JVM中生效;分布式部署的多台机器就需要使用分布式锁。

分布式锁其实就是在外部存储空间一个标签,当多台机器同时需要访问某个相同资源,就需要去竞争这把锁,谁竞争到锁谁就有权利访问这个资源,其他的机器就需要等待,当这台机器访问完成后就释放锁,其他的机器继续竞争锁,以此类推。

分布式锁的实现也很多,最常用的就是zookeeper和redis。这里又留下一个坑,等以后有时间再填。。。

缓存雪崩

缓存雪崩也是缓存穿透的特殊表现之一。

上面说的缓存击穿是一个热点key的失效,而缓存雪崩是多个热点key同时失效。

一般出现缓存雪崩的原因是:

  • 缓存过期的时间比较一致,某一时刻key大面积失效。解决办法:将缓存时间设置成一个随机数。
  • redis挂了,或因为网络抖动访问不了redis了。解决办法:使用redis集群。

解决方案一:数据预热,缓存时间随机

这种方式比较简单,可以专门做一个mysql和redis数据的同步服务,项目启动时,使用同步服务将mysql中的基础数据比如商户、门店等相关信息加载到redis中,并设置随机的失效时间;同时再每隔一个固定的时间(比如6h)同步一次。

如果要求修改mysql后,及时同步到redis,就需要有其他的措施保障了。

解决方案二:redis集群

为了防止redis挂掉,就要使用redis集群做高可用,可以将数据进行分片,将同样的数据分布到多台机器上;当集群中的一台或几台机器宕机,也依然能保障redis是可用的。

关于redis集群的高可用、数据一致性算法、hash环等问题,这里再留一个坑,后面我会抽时间补上的。。。

关注公众号,输入关键字"java-summary",可以获得源码。

传播知识,共享价值】,感谢小伙伴们的关注和支持,我是【诸葛小猿】,一个彷徨中奋斗的互联网民工。留下的坑,跪着我也会填完的!!!

redis 获取不到_redis系列之——缓存穿透、缓存击穿、缓存雪崩相关推荐

  1. Redis应用问题解决(缓存穿透、击穿、雪崩、分布式锁)

    Redis应用问题解决(缓存穿透.击穿.雪崩.分布式锁) 缓存穿透 问题描述 当系统中引入redis缓存后,一个请求进来后,会先从redis缓存中查询,缓存有就直接返回,缓存中没有就去db中查询,db ...

  2. Redis 缓存穿透、击穿、雪崩现象及解决方案

    前言 如何有效的理解并且区分 Reids 穿透.击穿和雪崩 缓存穿透 关键词:穿过 Redis 和数据库 当 Redis 和数据库中都没有我们想要的数据时,就需要考虑缓存穿透的问题了.下面这段逻辑大家 ...

  3. Redis缓存穿透、击穿、雪崩、概念及解决办法

    在生产环境中,会因为很多的原因造成访问请求绕过了缓存,都需要访问数据库持久层,虽然对Redsi缓存服务器不会造成影响,但是数据库的负载就会增大,使缓存的作用降低 一.缓存穿透 1.缓存穿透理解   缓 ...

  4. Redis 缓存穿透、击穿、雪崩 解决方法

    目录 引言 一.缓存穿透 1. 缓存穿透的原理 2. 解决方法 2.1 布隆过滤器 2.2 缓存空对象 二.缓存击穿 1. 缓存击穿原理 2. 解决方法 2.1 设置热点数据永不过期 2.2 加互斥锁 ...

  5. 牛逼,三句话搞懂 Redis 缓存穿透、击穿、雪崩!

    前言 如何有效的理解并且区分 Reids 穿透.击穿和雪崩之间的区别,一直以来都挺困扰我的.特别是穿透和击穿,过一段时间就稀里糊涂的分不清了. 为了有效的帮助笔者自己,以及拥有同样烦恼的朋友们区分这三 ...

  6. 缓存穿透、击穿、雪崩什么的傻傻分不清楚?看了这篇文后,我明白了

    对于缓存,大家肯定都不陌生,不管是前端还是服务端开发,缓存几乎都是必不可少的优化方式之一.在实际生产环境中,缓存的使用规范也是一直备受重视的,如果使用的不好,很容易就遇到缓存击穿.雪崩等严重异常情景, ...

  7. 缓存穿透、击穿、雪崩

    一.缓存穿透 缓存穿透是指缓存和数据库中均不存在目标数据,而用户不断发起请求,缓存也得不到更新,由此每次请求该数据都会到数据库.高并发量,就会对后端的 DB 系统造成很大压力.如查询 id 为&quo ...

  8. Redis_缓存穿透、击穿、雪崩

    查询步骤图解 1.缓存穿透 什么是缓存穿透:          查询请求一直向数据库查询,导致数据库压力过大,甚至奔溃         本质原因:查询的数据既不在缓存中,也不在数据库中 此时会出现:程 ...

  9. Redis缓存穿透、击穿、雪崩及主从复制

    文章目录 Redis缓存穿透 概念 解决方案1-布隆过滤器 解决方案2-缓存空对象 缓存击穿 概念 解决方案1-热点数据永不过期 解决方案2-加互斥锁 缓存雪崩 概念 解决方案1-Redis高可用 解 ...

  10. Redis缓存穿透、击穿、雪崩、预热、更新、降级

    Redis是高性能的分布式内存数据库,对于内存数据库经常会出现下面几种情况,也经常会出现在Redis面试题中:缓存穿透.缓存击穿.缓存雪崩.缓存预热.缓存更新.缓存降级.本篇分别介绍这些概念以及对应的 ...

最新文章

  1. 戴森最新吸尘器,竟用上了无人车和宇宙飞船黑科技
  2. APP元素事件操作API
  3. select 和epoll模型区别
  4. php页面转发,php如何实现页面路由转发
  5. Oracle 10g 高级安装图文教程(二)
  6. java 标准输入流 关闭 打开_java输出流关流疑问,以下这个程序的in和out是否要关闭?...
  7. 手机APP的秘密,看的一清二楚!
  8. 【NOI OpenJudge】【1.4】编程基础之逻辑表达式与条件分支
  9. 求n个排序链表的交集
  10. vim个性化设置---给脚本文件加注释头信息
  11. 正本清源:LBS(基于位置服务)技术——高精准IP地址定位的8大误区(下)
  12. 脑残式网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?
  13. Renderdoc截帧
  14. 校园文化建设计算机教室标语,【校园文化宣传墙】_校园文化建设墙上布置必备标语宣传画大全...
  15. Lua 公历转农历以及24节气计算
  16. solidworks报错合集
  17. 爬虫实战篇--更换代理ip
  18. 【hadoop】hbase配置
  19. 990php,音频牛绝配 DIY音频专用分立运放OP990初测
  20. 模块的接口类型LC,SC,FC 分别代表什么意思?

热门文章

  1. UML统一建模语UML2和EnterpriseArchitect
  2. BFS POJ 3126 Prime Path
  3. Hadoop实战之一~Hadoop概述
  4. 通过和函数名相同的字符串调用函数 --浅谈loadstring函数
  5. k8s集群搭建(一主多从)
  6. BeanDefinitionRegistryPostProcessor是什么?
  7. redis5.0.7集群搭建
  8. 如何保证缓存与数据库的双写一致性
  9. 从源码角度分析RocketMQ同步刷盘与异步刷盘的异同
  10. Go语言中的指针和new(T)函数的使用