SpringBoot+Mybatis整合:分布式缓存和本地缓存讲解以及Guava本地缓存使用实例

  • 什么是缓存

    • 程序经常要调用的对象存在内存中,方便其使用时可以快速调用,不必去数据库或者其他持久化设备中查询,主要就是提高性能
    • DNS缓存、前端缓存、代理服务器缓存Nginx、应用程序缓存(本地缓存、分布式缓存)、数据库缓存
  • 分布式缓存

    • 与应用分离的缓存组件或服务,与本地应用隔离一个独立的应用,多个应用可直接的共享缓存

    • 常见的分布式缓存 Redis、Memcached等

  • 本地缓存

    • 和业务程序一起的缓存,例如myabtis的一级或者二级缓存,本地缓存自然是最快的,但是不能在多个节点共享
    • 常见的本地缓存:myabtis 一级缓存、mybatis二级缓存;框架本身的缓存; redis本地单机服务;ehchche;guava cache、Caffeine等
  • 选择本地缓存和分布式缓存

    • 和业务数据结合去选择
    • 高并发项目里面一般都是有本地缓存和分布式缓存共同存在的

Guava Cache

以下示例未作缓存无数据操作

  • github地址:https://github.com/google/guava/wiki/CachesExplained
  • 全内存的本地缓存实现
  • 高性能且功能丰富
  • 线程安全,操作简单 (底层实现机制类似ConcurrentMap)

Step1:添加依赖

 <!--guava依赖包-->
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>19.0</version>
    </dependency>

Step2:创建工具类封装api方法

/**
 * Guava 本地缓存API
 */
@Component
public class BaseCache {

    /**
     * 分钟
     */
    private Cache<String,Object> tenMinuteCache = CacheBuilder.newBuilder()

            //设置缓存初始大小,应该合理设置,后续会扩容 底层也是HashMap的分段锁 无并发冲突
            .initialCapacity(10)
            //最大值   设置过大可能内存冲突
            .maximumSize(100)
            //并发数设置 同时多少个线程往里面写
            .concurrencyLevel(5)

            //缓存过期时间,写入后10分钟过期
            .expireAfterWrite(600, TimeUnit.SECONDS)

            //统计缓存命中率
            .recordStats()
            //构建类
            .build();

    /**
     * 小时
     */
    private Cache<String,Object> oneHourCache = CacheBuilder.newBuilder()

            //设置缓存初始大小,应该合理设置,后续会扩容 底层也是HashMap的分段锁 无并发冲突
            .initialCapacity(30)
            //最大值   设置过大可能内存冲突
            .maximumSize(100)
            //并发数设置 同时多少个线程往里面写
            .concurrencyLevel(5)

            //缓存过期时间,写入后1小时过期
            .expireAfterWrite(3600, TimeUnit.SECONDS)

            //统计缓存命中率
            .recordStats()
            //构建类
            .build();

    public Cache<String, Object> getOneHourCache() {
        return oneHourCache;
    }

    public void setOneHourCache(Cache<String, Object> oneHourCache) {
        this.oneHourCache = oneHourCache;
    }

    public Cache<String, Object> getTenMinuteCache() {
        return tenMinuteCache;
    }

    public void setTenMinuteCache(Cache<String, Object> tenMinuteCache) {
        this.tenMinuteCache = tenMinuteCache;
    }

}

Step3:实现类调取接口使用

下面的操作就是一个正常查询列表数据的操作,dao层什么的我就不往上放了

@Service
public class VideoBannerServiceImpl implements VideoBannerService {

    @Autowired
    private VideoBannerMapper videoBannerMapper;
    @Autowired
    private BaseCache baseCache;

    @Override
    public List<VideoBanner> listBanner() {

        try {
            //如果缓存找不到 去数据库里面找 回调函数 使用JDK8的写法
            Object cacheObj = baseCache.getTenMinuteCache().get(CacheKeyManager.INDEX_BANNER_KEY,()->{

                List<VideoBanner> videoBannerList = videoBannerMapper.listBanner();
                System.out.println("从数据库里面找");
                return videoBannerList;
            });
            //判断缓存中是否有值 未作缓存无数据操作
           	//走本地guave缓存
            if(cacheObj instanceof List){
                List<VideoBanner> videoBannerList = (List<VideoBanner>) cacheObj;

                return videoBannerList;
            }

        }catch (Exception e){
            e.printStackTrace();
        }
        //也可以返回兜底数据 如果是电商系统没什么意义 除非是新闻或者博客等项目还有用
        return null;
    }
}

关于测试就是调取两次相同查询接口,控制台查看一下mysql的日志打印,如果只出现一条,说明缓存实现

猜你喜欢

转载自blog.csdn.net/q736317048/article/details/111599686
今日推荐