当前位置: 首页 > 工具软件 > xxl-cache > 使用案例 >

缓存Cache概述——缓存Cache&&1.1.1

封梓
2023-12-01

1、为什么要使用缓存

    越靠近CPU的存储容量越小但访问速度越快。

    CPU寄存器-->CPU高速缓存-->内存-->硬盘

  • CPU寄存器(几十到几百字节):位于CPU执行单元中。寄存器是访问速度最快的存储器。典型的访问时间是一纳秒以内
  • CPU高速缓存(Cache):位于CPU内核中。Cache通常分为两级Cache:一级Cache(几十到几百KB)靠近CPU执行单元;二级Cache(几百KB到几MB)靠近物理内存。典型的访问时间是几纳秒到几十纳秒
  • 内存(几GB到几十GB):位于CPU外的存储芯片,通过内存管理模块及数据总线与CPU相连。典型的访问时间是一百多纳秒
  • 硬盘(几百GB到几TB):位于设备总线上,并不直接和CPU相连,CPU通过设备总线的控制器访问硬盘。典型的访问时间是几毫秒

     CPU寄存器、CPU高速缓存、内存都是非持久存储设备,而且容量受限,大多数软件处理的数据是需要保存到硬盘上的,但处理数据时又需要将数据从硬盘读取到内存中,为了解决硬盘速度慢的“瓶颈”问题,可以模仿CPU高速缓存的设计思路,将读多写少、一致性要求较低的数据在内存中也保存一份,也就是把数据缓存起来。

2、缓存的实现方案

  • 本地缓存:缓存位于应用软件进程内,最简单的可以通过一个Map来同步存储数据库中的某些热点数据,需要读取的时候直接从Map中读取。专业的缓存组件Google的Guava、Terracotta的Ehcache,可以提供缓存淘汰、缓存同步等管理功能。
  • 集群缓存:单机的话,本地缓存就可以解决问题。但是大多数生产环境中应用软件是要多节点部署形成集群的,这时缓存也要同步的形成缓存集群,以避免缓存数据在多个节点之间的不一致。Ehcache就可以组件集群,它会同步各节点的缓存数据。
  • 分布式缓存:集群缓存可以解决多个缓存节点的数据同步问题,但是当节点过多时(一般不超过四个)会造成内存及网络资源的浪费,这个时候采用分布式缓存,也就是缓存和应用程序不在一个进程内。比如:Redis、memcached就是这样的缓存中间件。

3、常见的缓存问题 

(1)双写不一致

       数据库和缓存双写,就必然会存在不一致的问题。如果对数据有强一致性要求,不能放缓存。即使做其他补充方案,也只能保证最终一致性。

  • 基本的缓存不一致问题 

         先删除缓存,再更新数据库。

  • 高并发下的缓存不一致问题

        如:1号线程删除缓存,更新;2号从数据库读取数据,放入缓存;1号提交事务,这个时候缓存里边放的是旧数据。

        解决办法:①数据写请求串行化(即:四个操作都放在一个队列中由独立线程取操作) 

                          ② 对缓存和数据库的更新加锁

 (2)缓存并发竞争

        客户端同时并发写一个key,可能本来应该先到的数据后到了,导致数据版本出错;或者是多客户端同时获取一个key,修改值之后再写回去,只要顺序错了,数据就错了。

        解决方案:写数据时采用CAS类的乐观锁方案加锁。

(3)缓存雪崩

        是指在某一个时间段,缓存集中过期失效或者缓存系统出现故障,而导致数据读取请求全部压在了数据源层。

        解决方案:

  • 高可用:主从+哨兵,避免全盘奔溃。
  • 缓存架构:本地ehcache缓存+hystrix限流&降级、分散缓存过期时间,避免MySQL被打死。
  • 缓存持久:Redis持久化,一旦重启,自动从磁盘上加载数据,快速回复缓存数据。

(4) 缓存穿透

        是指查询一个数据源中一定不存在的数据

        解决方案:

  • 缓存空值:也就是如果从数据库查询的对象为null,也放入缓存,只是设定的缓存过期时间较短,比如:60s
  • 接口层增加校验:如用户鉴权校验,id做基础校验,id<=0的直接拦截

(5) 缓存击穿

        某个key非常热点,访问非常频繁,出于集中式高并发访问的情况,当这个key在失效的瞬间,大量的请求就击穿缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。

        解决方案:

  • 若缓存的数据基本不会发生更新时,则可尝试将该热点数据设置为永不过期。
  • 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于Redis等分布式中间件的分布式互斥锁,以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
 类似资料: