本地缓存Caffeien

为什么用本地缓存


低延迟高吞吐:本地缓存直接存储在JVM堆内存中,读写操作无需网络传输,延迟可低至纳秒级,显著优于Redis等分布式缓存的毫秒级响应 。
降低Redis压力:在多级缓存架构中,本地缓存作为第一层缓存,可过滤大部分请求,仅将未命中数据转发至Redis等分布式缓存,降低后端服务和Redis压力。

 

为什么要用Caffeine

Caffeine号称本地缓存之王,SpringBoot 1.x 默认是Guava cache,Spring Boot 2.x默认是Caffeien
吞吐量领先:官方基准测试显示,Caffeine的吞吐量比Guava Cache高40%以上,尤其在高并发场景下表现突出。
并发优化:采用Java 8的StampedLock锁技术,减少锁竞争,提升多线程环境下的并发处理能力。
多种过期策略:支持写后过期(expireAfterWrite)、访问后过期(expireAfterAccess)及自定义过期逻辑,适应不同业务需求
容量控制:提供基于条目数(maximumSize)、权重(maximumWeight)或引用(弱引用/软引用)的灵活容量管理

 

Guava Cache
Guava Cache 是 Google 提供的 Java 缓存库
简单易用的 API、功能相对比价丰富。

EhCache
EhCache提供了非常丰富的功能,不但可以将数据存储在JVM内部,还可以放到堆外(供了堆外缓存off-heap)

 

本地缓存的刷新/过期策略

 

refreshAfterWrite(写后刷新):‌在数据被写入缓存后,经过指定的时间间隔,如果数据被再次访问,会触发异步刷新操作‌‌ ,在刷新完成前,所有访问请求会立即返回当前缓存中的旧值
refreshAfterAccess(访问后刷新):‌在数据被访问后,经过指定的时间间隔,如果数据被再次访问,会触发异步刷新操作,在刷新完成前,所有访问请求会立即返回当前缓存中的旧值

expireAfterWrite(基于写入时间过期): 从缓存条目‌创建或最后一次被写入‌时开始计时,超过设定的时间后,该条目会被自动移除‌,数据更新使用加锁方式。
expireAfterAccess(基于访问时间过期): 从缓存条目‌最后一次被访问‌时开始计时,超过设定时间后条目失效‌,‌,数据更新使用加锁方式。

 

数据在expireAfterWrite和expireAfterAccess过期后,新的请求过来caffeine是如何执行的

 

‌检查与移除过期条目‌:当请求访问一个缓存键(Key)时,Caffeine 会首先检查该条目是否已因 expireAfterWrite 而过期。如果已过期,它会立即将这个旧条目从缓存中移除

同步互斥回源加载‌:在移除旧数据后,Caffeine 会为该缓存键获取一个‌同步锁‌。第一个成功获取锁的请求(假设是请求A)将负责执行回源逻辑(例如,通过 CacheLoader 或 Callable 从数据库查询新数据)。

阻塞等待与结果返回‌:在请求A持有锁并加载数据期间,后续任何访问同一键的并发请求(如请求B)会被阻塞,并等待请求A完成操作。

‌数据回填与锁释放‌:请求A成功获取新数据后,会将其存入缓存,然后释放同步锁。此时,所有被阻塞的等待线程(如请求B)会立即被唤醒,并获取到请求A刚加载的新数据。

 

数据在refreshAfterWrit和refreshAfterAccess过期后,新的请求过来caffeine是如何执行的

‌检查刷新条件‌:当请求访问一个缓存键时,Caffeine 会检查该条目自上次写入或刷新后经过的时间是否已经超过了 refreshAfterWrite 设定的阈值需要注意的是,达到刷新时间并不意味着数据已过期(如果同时还配置了更长的 expireAfterWrite,数据可能仍然有效)

‌触发异步刷新‌:如果满足刷新条件,Caffeine 会为这个缓存键触发一个‌异步刷新操作‌,这个操作默认由 ForkJoinPool.commonPool() 执行,也可以通过 Caffeine.executor(Executor) 方法指定自定义的线程池
立即返回旧值‌:在异步刷新操作执行期间,触发刷新的请求以及后续访问同一键的所有请求,都‌不会等待新数据加载完成‌,而是会立即返回当前缓存中持有的旧值,。这是 refreshAfterWrite 与 expireAfterWrite 在行为上的一个关键区别
‌异步加载与更新‌:在后台线程中,Caffeine 会执行 CacheLoader.reload 方法(如果已重写)或默认的 CacheLoader.load 方法来获取新数据,一旦新数据成功加载,它会自动更新到缓存中,替换掉旧值。此后,新的请求将获取到更新后的数据

 

refreshAfterWrite 与 expireAfterWrite 的区别‌:


refreshAfterWrite 关注的是数据的‌新鲜度‌,它异步地更新数据而不阻塞请求,可能会造成短暂时间获取到的是旧址。

expireAfterWrite 关注的是数据的‌有效性‌,当数据过期后,访问请求会同步地回源加载,导致线程阻塞直到新数据返回。

 

过期数据管理

数据惰性删除:惰性删除是指 Caffeine 不会在数据过期时立即将其从缓存中移除,而是等到‌下次访问该数据时‌才会检查其是否过期。如果发现数据已过期,Caffeine 会先将其标记为无效并从缓存中移除,然后再触发回源操作(如从数据库加载新数据)并更新缓存。‌这种机制避免了不必要的同步淘汰操作,减少了主线程的阻塞,从而提升了并发性能
定时清理:为了及时清理那些长期未被访问的过期数据,Caffeine 会启动一个‌后台定时任务‌(默认约每秒执行一次),主动扫描缓存并移除已过期的条目。 这种定期清理机制与惰性删除相结合,既保证了缓存访问的高效性,又避免了内存中堆积大量无效数据。

 


本地缓存数据不一致问题

1.多台机器数据同步:使用MQ进行消息广播通知进行实时变更。
2.先更新库再删除缓存,或者使用延迟双删,延迟双删是解决并发情况下的极端情况,延迟时间需要控制好。

 

posted @ 2025-11-16 17:52  爵士灬  阅读(24)  评论(0)    收藏  举报