springboot 缓存一致性常用解决方案( 二 )

spring:redis:host: localhostport: 6379cache:type: redis3、几个核心接口1)根据用户ID获取用户@GetMapping("/getById")public DbUser getById(String id){return dbUserService.getById(id);@Override@Cacheable(value = https://www.isolves.com/it/cxkf/kj/2022-09-05/{"dbUser"},key = "#root.args[0]",cacheManager = "cacheManagerMinutes")public DbUser getById(String id) {System.out.println("首次查询走数据库");DbUser dbUser = dbUserMapper.getByUserId(id);return dbUser;2)根据用户ID查询用户,并缓存到JVM;@GetMapping("/getByIdFromCaffeine")public DbUser getByIdFromCaffeine(String id){return dbUserService.getByIdFromCaffeine(id);@Override@Cacheable(value = https://www.isolves.com/it/cxkf/kj/2022-09-05/{"dbUser"},key = "#root.args[0]",cacheManager = "caffeineCacheManager")public DbUser getByIdFromCaffeine(String id) {System.out.println("查询数据库");DbUser dbUser = dbUserMapper.getByUserId(id);System.out.println("第一次走缓存");return dbUser;3)根据用户ID删除用户;@GetMapping("/deleteById")public String deleteById(String id){return dbUserService.deleteById(id);@Override@CacheEvict(value = https://www.isolves.com/it/cxkf/kj/2022-09-05/{"dbUser"},key = "#root.args[0]",cacheManager = "cacheManagerMinutes")public String deleteById(String id) {dbUserMapper.deleteByUserId(id);return "delete success";4、功能测试 
首先在数据库的 db_user 表准备一条测试数据
分别调用查询用户接口
1、 http://localhost:8083/getById?id=1 ;
多次刷新接口,sql只输出了一次

springboot 缓存一致性常用解决方案

文章插图
 
2、 http://localhost:8083/getByIdFromCaffeine?id=1
springboot 缓存一致性常用解决方案

文章插图
 
多次刷新接口,sql只输出了一次
springboot 缓存一致性常用解决方案

文章插图
 
从上面的结果可以看到,我们模拟了查询数据分别缓存到了JVM内存和redis的效果,接下来,删除当前这条数据,执行下面的接口
3、 http://localhost:8083/deleteById?id=1
springboot 缓存一致性常用解决方案

文章插图
 
springboot 缓存一致性常用解决方案

文章插图
 
再次调用第一个查询用户的接口,无返回数据,表明redis中缓存的结果被清理了,这是我们使用了springcache后,通过 CacheEvict 这个注解,会自动帮我们管理redis中的缓存;
但这时,再次调用查询JVM缓存的接口,发现仍然可以从本地缓存中得到数据
4、 http://localhost:8083/getByIdFromCaffeine?id=1
springboot 缓存一致性常用解决方案

文章插图
 
基于上面的测试结果,可以看到,缓存一致性的问题就产生了,这里我故意将本地缓存的时间调整的长了一点,实际开发过程中,建议本地缓存的时间一般不要超过1分钟;
三、解决方案一:清理redis缓存,同步清理本地缓存1、增加一个本地缓存操作的工具类import com.congge.config.SpringContextHolder;import org.springframework.cache.Cache;import org.springframework.cache.CacheManager;import java.util.Objects;public class CaffeineCacheUtils {private static CacheManager cm;static {cm = SpringContextHolder.getBean("caffeineCacheManager");* 添加缓存* @param cacheName 缓存名称* @param key 缓存key* @param value 缓存值public static void put(String cacheName, String key, Object value) {Cache cache = cm.getCache(cacheName);cache.put(key, value);* 获取缓存* @param cacheName 缓存名称* @param key 缓存key* @returnpublic static Object get(String cacheName, String key) {Cache cache = cm.getCache(cacheName);if (cache == null) {return null;return Objects.requireNonNull(cache.get(key)).get();* 获取缓存(字符串)* @param cacheName 缓存名称* @param key 缓存key* @returnpublic static String getString(String cacheName, String key) {Cache cache = cm.getCache(cacheName);if (cache == null) {return null;Cache.ValueWrapper wrapper = cache.get(key);if (wrapper == null) {return null;return Objects.requireNonNull(wrapper.get()).toString();* 获取缓存(泛型)* @param cacheName 缓存名称* @param key 缓存key* @param clazz 缓存类* @param 返回值泛型* @returnpublic static T get(String cacheName, String key, Class clazz) {Cache cache = cm.getCache(cacheName);if (cache == null) {return null;Cache.ValueWrapper wrapper = cache.get(key);if (wrapper == null) {return null;return (T) wrapper.get();* 清理缓存* @param cacheName 缓存名称* @param key 缓存keypublic static void evict(String cacheName, String key) {Cache cache = cm.getCache(cacheName);System.out.println(cache.getName());if (cache != null) {cache.evict(key);


推荐阅读