这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天
今天学习了缓存相关的知识,这里学习的知识并不仅限于我们耳熟能详的Redis缓存,还包含缓存相关的思想以及对应缓存使用的模型和实战场景
缓存相关知识
-
缓存漂移:由于负载均衡的操作使得每次刷新到不同的服务器上,而不同的服务器上的缓存不同
-
为了解决缓存漂移,采用了许多服务器共用集中式缓存,但是由于因为对
集中式缓存的过分滥用!分析发现这些流程的处理需要涉及大量的交互与数据整合逻辑,一个流程需要访问近乎30次Redis!虽然Redis的单次请求处理性能极高,甚至可以达到微秒级别的响应速度,但是每个流程里面几十次的网络IO交互,导致频繁的IO请求,以及线程的阻塞与唤醒切换交替,使得系统在线程上下文切换层面浪费巨大 -
解决上述问题,最常规的手段便是尝试降低对集中式缓存(如Redis)的请求数量,降低网络IO交互次数。而如何来降低呢? —— 又回到了本地缓存!集中式缓存并非是分布式系统中提升性能的银弹,但我们可以将本地缓存与集中式缓存结合起来使用,取长补短,实现效果最大化。如图所示:
-
对于一些变更频率比较高的数据,采用
集中式缓存,这样可以确保数据变更之后所有节点都可以实时感知到,确保数据一致;对于一些极少变更的数据(比如一些系统配置项)或者是一些对短期一致性要求不高的数据(比如用户昵称、签名等)则采用
本地缓存,大大减少对远端集中式缓存的网络IO次数。
-
-
缓存不止Redis
- 网络传输场景。比如
ARP协议,基于ARP缓存表进行IP与终端硬件MAC地址之间的缓存映射。这样与对端主机之间有通信需求的时候,就可以在ARP缓存中查找到IP对应的对端设备MAC地址,避免每次请求都需要去发送ARP请求查询MAC地址。 - MyBatis的多级缓存
MyBatis作为JAVA体系中被广泛使用的数据库操作框架,其内部为了提升处理效率,构建了一级缓存与二级缓存,大大减少了对SQL的重复执行次数。 - CPU中的缓存。
CPU与内存之间有个临时存储器(高速缓存),容量虽比内存小,但是处理速度却远快于普通内存。高速缓存的机制,有效地解决了CPU运算速度与内存读写速度不匹配的问题。
- 网络传输场景。比如
-
构建缓存对于IO方面的益处
- 从自身性能层面考虑,减少对外
IO操作,降低了对外接口的响应时延,也对服务端自身处理性能有一定提升。 - 从对端服务稳定性层面考虑,避免对端服务
负载过大。很多时候调用方和被调用方系统的承压能力是不匹配的,甚至有些被调用方系统可能是不承压的。为了避免将对端服务压垮,需要调用方缓存请求结果,降低IO请求。 - 从自身可靠性层面而言,将一些远端服务请求到的结果缓存起来,即使远端服务出现故障,自身业务依旧可以基于缓存数据进行正常业务处理,起到一个
兜底作用,提升自身的抗风险能力
- 从自身性能层面考虑,减少对外
-
SpringCloud家族的Eureka,或者是Alibaba开源的Nacos。它们对于缓存的利用,可以说是对上面所提几点的完美阐述。
-
缓存的结构模式
-
旁路型缓存:业务自行负责与缓存以及数据库之间的交互,可以自由决定缓存未命中场景的处理策略,更加契合大部分业务场景的定制化诉求
- 由于业务模块自行实现缓存与数据库之间的数据写入与更新的逻辑,实际实现的时候需要注意下在高并发场景的
数据一致性问题,以及可能会出现的缓存击穿、缓存穿透、缓存雪崩等问题的防护。
- 由于业务模块自行实现缓存与数据库之间的数据写入与更新的逻辑,实际实现的时候需要注意下在高并发场景的
-
穿透型缓存:在实际业务中使用的较少,主要是应用在一些缓存类的中间件中,或者在一些大型系统中专门的数据管理模块中使用。
- 一般情况下,业务使用缓存的时候,会是先尝试读取缓存,在尝试读取
DB,而使用穿透型缓存架构时,会有专门模块将这些动作封装成黑盒的,业务模块不会与数据库进行直接交互。这种模式对业务而言是比较友好的,业务只需调用缓存接口即可,无需自行实现缓存与DB之间的交互策略。
- 一般情况下,业务使用缓存的时候,会是先尝试读取缓存,在尝试读取
-
异步型缓存。其主要策略就是业务侧请求的实时读写交互都是基于缓存进行,任何数据的读写也完全基于缓存进行操作。此外,单独实现一个数据持久化操作(独立线程或者进程中执行),用于将缓存中变更的数据写入到数据库中。
- 实时业务读写请求完全基于缓存进行,而将数据库仅仅作为一个数据持久化存储的备份盘。由于实时业务请求仅与缓存进行交互,所以在性能上可以得到更好的表现。但是这种模式也存在一个致命的问题:数据可靠性!因为是异步操作,所以在下一次数据写入DB前,会有一段时间数据仅存在于缓存中,一旦缓存服务宕机,这部分数据将会丢失。所以这种模式仅适用于对数据一致性要求不是特别高的场景
-
-
缓存在使用过程中推荐满足的标准
-
可删除重建:这是缓存与持久化存储最大的一个差别。缓存的定位一定是为了辅助业务处理而生的,也就是说缓存有则使用,没有也不会影响到我们具体的业务运转。
-
兜底与自恢复机制:
- 关注下缓存数据量超出承受范围的处理策略,比如定好数据的
淘汰机制。 - 避免缓存集中失效,比如批量加载数据到缓存的时候
随机打散过期时间,避免同一时间大批量缓存失效引发缓存雪崩问题。 - 有效地冷数据预热加载机制,以及热点数据防过期机制,避免出现大量对冷数据的请求无法命中缓存或者热点数据突然失效,导致
缓存击穿问题。 - 合理的防身自保手段,比如采用
布隆过滤器机制,避免被恶意请求攻陷,导致缓存穿透类的问题
- 关注下缓存数据量超出承受范围的处理策略,比如定好数据的
-
数据一致性问题,确保缓存中数据和数据库中的数据一致性
- 一般数据库往往使用事务来保证数据的一致性问题,然而在一些并发请求特别高的时候,基于事务控制来保证数据一致性往往会对性能造成影响,且事务
隔离级别设置的越高影响越大,所以也可以采用一些其它辅助策略,来替代事务的控制,如重试机制、或异步补偿机制、或多者结合方式等。
- 一般数据库往往使用事务来保证数据的一致性问题,然而在一些并发请求特别高的时候,基于事务控制来保证数据一致性往往会对性能造成影响,且事务
-