当前位置: 首页 > news >正文

【Java面试】一、Redis篇(上)

文章目录

  • 0、准备
  • 1、缓存穿透:不存在的key
  • 2、缓存击穿:热点key过期
  • 3、缓存雪崩:大批key同时过期
  • 4、双写一致性
    • 4.1 要求高一致性
    • 4.2 允许一定的一致延迟
  • 5、面试

0、准备

Redis相关概览:

在这里插入图片描述

以简历上所列的项目为切入点,展开Redis相关的问题:

在这里插入图片描述

1、缓存穿透:不存在的key

Client ⇒ Redis ⇒ MySQL,缓存穿透,即中间的Redis形同虚设,请求每次过来都查库。一般是网站被攻击时,疯狂造不存在数据,然后发起请求,冲击数据库,消耗数据库连接池资源,直到服务不可用。

在这里插入图片描述

解决方案一:缓存空数据

在这里插入图片描述

查到的结果为空也写进Redis。实现简单但消耗内存,可能会发生不一致的问题(即库里有数据了,但缓存里依旧是null,导致查不到最新数据)

解决方案二:布隆过滤器

缓存预热的时候,往布隆过滤器中添加数据。后面请求过来时,先经过布隆过滤器,判断ID不存在的话,直接返回,都走不到Redis。

在这里插入图片描述
如此,内存占用少,但实现复杂,且存在误判(即有的ID不存在,但布隆过滤器会判断为存在)

在这里插入图片描述
bitmap(位图):一个bit数组,每个单元非0即1。预热ID的时候,用多个hash函数获取该ID的hash值,并把bitmap对应位置改为1。后面查数据是否存在时,就用相同的hash函数获取hash值,查看对应的位置是否都为1。由此,布隆过滤器实现了检索一个元素是否在一个集合中。

在这里插入图片描述

如上,id1和id2点亮了1、3、7和9、12、15的位置为1,id3虽然不存在,但其hash值所在位置都为1,判定为是存在的ID,即误判。数组越大,误判率越低。

2、缓存击穿:热点key过期

给一个热点key设置了过期时间,当这个key过期的时候,恰好有大量请求查这个key,这些请求自然都走到了数据库,可能导致DB服务不可用。如下图,比如查询一次MySQL再写到Redis需要50ms,热点数据过期后,这50ms内的大量请求都会冲到数据库

在这里插入图片描述

解决方案一:添加互斥锁

在这里插入图片描述
缓存未命中,查库前,获取一个互斥锁,获取不到则一直重试,如此,后面的线程走不到查库这一步,直到线程1完成查库+写入Redis,其余线程就会命中缓存。如此,保护了数据库服务且保证了数据强一致性,但性能不好

解决方案二:逻辑过期

Redis层面,不设置过期时间,但业务层面加一个过期时间,如

keyvalue
001{“id”:“123”,“title”:“标题1”,"expire":1716368322}

在这里插入图片描述
即明天16:58的时候,这条数据逻辑过期,但你在Redis中还能查到,不过是查到了过期数据,不是最新的。具体流程:

在这里插入图片描述
线程1过来查缓存,发现当前时间超过了缓存的逻辑过期时间,于是线程1获取一个互斥锁,再开启一个线程2去查库 + 更新缓存,自己则先把过期数据返回。此过程中,即使有线程3进来,并发现了已超过逻辑过期时间,它也不会重复去查库重建缓存,因为它获取不到互斥锁,此时线程3会先返回过期数据。 因此,这种方式性能好,但数据一致性不保证。

3、缓存雪崩:大批key同时过期

同一时间段,大量缓存的key过期,或者Redis服务宕机,导致大量请求冲到数据库
在这里插入图片描述

解决思路是:给不同的key添加随机的TTL(过期时间),如此,大量的key不会同时过期。至于Redis宕机,则可以用Redis哨兵模式或者集群模式、给业务添加多级缓存等方式解决。

最后,降级限流,可用于解决缓存穿透、击穿、雪崩的解决。即服务请求失败次数到一定阈值,直接走降级策略(比如不查库,直接返回空)

//Tips:穿透无中生有key,布隆过滤null隔离缓存击穿过期key,锁与非期解难题雪崩大量过期key,过期时间要随机面试必考三兄弟,可用限流来保底

4、双写一致性

相关问题:MySQL和Redis的数据如何进行同步。两种追求:

  • 一致性高
  • 允许一定的一致延迟

4.1 要求高一致性

在这里插入图片描述

读操作:命中直接返回。未命中缓存则 查库 + 写入缓存

写操作:数据库更新时,进行延时双删

在这里插入图片描述
有数据库的写操作时,为了数据一致性,如果不双删,只删一次缓存 + 改库,则实现方式可以是:

  • 先删缓存,再改库
  • 先改库,再删缓存
1)若先删缓存,再改库:

理想状态为:线程1删缓存,再更新数据库,后面请求过来(对应线程2),先查缓存,未命中,去查库 + 写入缓存,一切正常

在这里插入图片描述

再看非理想状况:线程1删除缓存后挂起,此时缓存为null,库中为10。然后新的请求进来(对应线程2),CPU切到线程2执行,查缓存未命中,线程2去查库 + 写入缓存,线程2执行结束,此时缓存为10,库中为10。最后线程1解除挂起,继续执行,线程1去更新了数据库,此时缓存为10,库中为20 ,数据不一致!

在这里插入图片描述

总之,高并发下,线程交替执行,这样实现会出现数据不一致问题。

2)若先改库,再删缓存

理想状态为:线程2更新数据库,再删缓存,此时缓存为null,库中为20。后面请求过来(对应线程1),先查缓存,未命中,去查库 + 写入缓存,一切正常

在这里插入图片描述

非理想状态下:比如一开始缓存过期,即缓存中为null,库中为10,线程1进来未命中缓存,查库得到10,然后挂起。请求2进来(对应线程2),其进行了update,改库,并删缓存,此时,库中为20,缓存中为null。线程2执行结束,线程1解除挂起继续执行,将查到的数据,写入缓存。此时缓存中为10,库中为20,数据不一致。

在这里插入图片描述

因此,不管先删缓存还是先改库,都可能出现数据不一致

==> 延时双删:先删缓存,数据库更新完后,再删一次缓存。至于为什么要延时后第二次删,是因为如果数据库是主从模式,读写分离,那就需要等主节点把数据同步到从节点。但这个延时的大小不好控制,还是可能出现脏数据。想强一致,可用分布式锁。

在这里插入图片描述
一个线程在更新数据前,加锁,update库 + 删缓存后,释放锁。期间,其余线程不能读写。以上可优化为:读数据的时候,添加共享锁,读读共享,读写互斥。写数据的时候,加排他锁,阻塞其他线程的读和写。如此,实现数据强一致性,但性能低。

4.2 允许一定的一致延迟

异步通知,保证数据的最终一致性。业务服务更新库后,发消息到MQ,缓存服务监听MQ,去更新缓存

在这里插入图片描述
以上实现对业务代码有一定的侵入性,需要添加发消息的代码。可把MQ替换为Canal:

在这里插入图片描述

阿里的Canal基于MySQL的主从同步实现。数据库一旦发生改变,二进制的binlog记录DDL语句和DML语句,Canal伪装成MySQL的一个从节点,监听读取MySQL的binlog去更新缓存。此方式不用改业务代码,无侵入性。

最后,如果不要求实时性和强一致性,如热点文章数据,可用异步方案同步数据。如果要求数据强一致性,如抢券的库存,则采用redisson提供的读写锁保证数据同步。

5、面试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

相关文章:

  • 链表-设计LRU缓存结构
  • uni-app App端实现文字语音播报(Ba-TTS)
  • PTA 6-4 配对问题
  • 如何参与github开源项目并提交PR
  • Linux下环境变量配置出错导致基础命令使用不了的问题解决
  • 抖音分享链接视频下载
  • [Algorithm][动态规划][简单多状态DP问题][按摩师][打家劫舍Ⅱ][删除并获得点数][粉刷房子]详细讲解
  • 手机相册的照片彻底删除了怎么恢复?删除照片恢复的5种方法
  • 甘肃教育杂志社-甘肃教育编辑部
  • CSP俄罗斯方块(简单易懂)
  • C语言笔记21 •模拟atoi函数•
  • conda常见命令
  • 汽车R155法规中,汽车获取到的VTA证书,E后面的数字表示什么意思?
  • MySQL入门学习-查询进阶.别名
  • 携手AI,如何共赢未来?
  • [译]前端离线指南(上)
  • __proto__ 和 prototype的关系
  • Apache的80端口被占用以及访问时报错403
  • Dubbo 整合 Pinpoint 做分布式服务请求跟踪
  • iOS编译提示和导航提示
  • Javascript基础之Array数组API
  • js写一个简单的选项卡
  • mac修复ab及siege安装
  • PAT A1017 优先队列
  • Vue学习第二天
  • 阿里云容器服务区块链解决方案全新升级 支持Hyperledger Fabric v1.1
  • 得到一个数组中任意X个元素的所有组合 即C(n,m)
  • 复习Javascript专题(四):js中的深浅拷贝
  • 经典排序算法及其 Java 实现
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 实战|智能家居行业移动应用性能分析
  • 使用权重正则化较少模型过拟合
  • 微信小程序开发问题汇总
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 学习笔记:对象,原型和继承(1)
  • 自制字幕遮挡器
  • 白色的风信子
  • ![CDATA[ ]] 是什么东东
  • #QT(串口助手-界面)
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (2)空速传感器
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (Ruby)Ubuntu12.04安装Rails环境
  • (动态规划)5. 最长回文子串 java解决
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (七)glDrawArry绘制
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (五)activiti-modeler 编辑器初步优化
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (转)EOS中账户、钱包和密钥的关系
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • ./configure,make,make install的作用(转)
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .jks文件(JAVA KeyStore)