Dubbo笔记六:进程缓存GuavaCache的使用


文章目录

  • Dubbo笔记六:进程缓存GuavaCache的使用
  • 缓存的好处和坏处
  • 缓存设计
  • Google GauvaCache的使用
  • HashTable和HashMap和LoadingCache的区别


缓存的好处和坏处
  1. 好处

1、缓存加速读写速度

2、降低后端负载

  1. 缓存的坏处

1、数据不一致:缓存层和数据层有时间窗口不一致,和更新策略有关。

2、代码维护成本:需要开发人员维护缓存。增加代码复杂度

3、堆内缓存可能带来内存溢出的风险影响用户进程,如ehCache、loadingCache

  1. mysql的执行流程图

dubbo缓存实现原理 dubbo 缓存_List

缓存设计
  1. 缓存的使用

先从缓存中查询数据,如果没有再从数据库中查数据。

  1. 缓存接入图

dubbo缓存实现原理 dubbo 缓存_缓存_02

Google GauvaCache的使用
  1. GauvaCache的使用,也就是loadingCache
  2. 先引入依赖
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.0-jre</version>
</dependency>
  1. 使用缓存获取所有TCoupon的内容
LoadingCache<Integer, List<TCoupon>> couponCache= CacheBuilder.newBuilder()
    //设置过期时间,和异步刷新时间
    .expireAfterWrite(10, TimeUnit.MINUTES).refreshAfterWrite(5,TimeUnit.MINUTES)
    //build就是从磁盘里面load数据
    .build(new CacheLoader<Integer, List<TCoupon>>() {

        @Override
        public List<TCoupon> load(Integer o) throws Exception {
			return loadCoupon(o);
        }
    });

private List<TCoupon> loadCoupon(Integer o){
    TCouponExample example=new TCouponExample();
    example.createCriteria().andStatusEqualTo(Constant.USERFUL)
        .andStartTimeLessThan(new Date()).andEndTimeGreaterThan(new Date());
    return tCouponMapper.selectByExample(example);
}

public List<TCoupon> getCouponList(){
    List<TCoupon> tCoupons= Lists.newArrayList();
    try {
        tCoupons=couponCache.get(1);
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    return tCoupons;
}
  1. 多id查询

1、同时传来多个ids,分别以逗号隔开

2、分割ids,进行遍历查询

3、首先从缓存中查询,如果缓存中没有该数据,就将该id放到一个listA里面

4、如果缓存中有,就放到一个要返回的listB里面。

5、从listA里面取出id,进行数据库查询,将查询的结果添加到缓存中

6、在将查询的结果添加到listB中。

7、返回结果listB

LoadingCache<Integer, TCoupon> couponIdsCache= CacheBuilder.newBuilder()
    //设置过期时间,和异步刷新时间
    .expireAfterWrite(10, TimeUnit.MINUTES).refreshAfterWrite(5,TimeUnit.MINUTES)
    //build就是从磁盘里面load数据
    .build(new CacheLoader<Integer, TCoupon>() {

        @Override
        public TCoupon load(Integer o) throws Exception {
            return loadIdCoupon(o);
        }
    });

private TCoupon loadIdCoupon(Integer id) {
    return tCouponMapper.selectByPrimaryKey(id);
}
public List<TCoupon> getCouponByIds(String ids){
    String[] idStr = ids.split(",");
    List<Integer> loadFromDB=Lists.newArrayList();
    List<TCoupon> tCoupons=Lists.newArrayList();
    List<String> idList = Lists.newArrayList(idStr);
    for(String id:idList ){
        //getIfpresent()会查询缓存,如果没有,不会触发查询数据库。会造成单个查询,效率降低
        TCoupon tCoupon = couponIdsCache.getIfPresent(id);
        if(tCoupon==null){
            loadFromDB.add(Integer.parseInt(id));
        }else{
            tCoupons.add(tCoupon);
        }
    }
    List<TCoupon> tCoupons1 = couponByIds(loadFromDB);
    Map<Integer, TCoupon> tCouponMap = tCoupons1.stream().collect(Collectors.toMap(TCoupon::getId, TCoupon -> TCoupon));
    tCoupons.addAll(tCoupons1);
    //将返回结果回写的缓存
    couponIdsCache.putAll(tCouponMap);
    return tCoupons;
}
private List<TCoupon> couponByIds(List<Integer> ids){
    TCouponExample example=new TCouponExample();
    example.createCriteria().andIdIn(ids);
    return tCouponMapper.selectByExample(example);
}

private List<TCoupon> loadCoupon(Integer o){
    TCouponExample example=new TCouponExample();
    example.createCriteria().andStatusEqualTo(Constant.USERFUL)
        .andStartTimeLessThan(new Date()).andEndTimeGreaterThan(new Date());
    return tCouponMapper.selectByExample(example);
}
HashTable和HashMap和LoadingCache的区别
  1. HashTable底层采用了Synchronized锁的方式,也就是假如里面有16个位置,当第一个来取得时候会给Hashtable添加锁,这个时候其他线程无法进入Hashtable取值,即使这个值在其他位置也不能取值,除非获取锁。
  2. 而LoadingCache采用的是分段锁,也就是CorrentHashMap里面假如有16个位置,每个位置都有一把锁,当线程获取一把锁时不影响其他线程去获取其他位置的锁。这就大大提高了效率。这也是为什么不用HashMap做缓存的原因。
  3. 那么为什么不直接用ConcurrentHashMap呢?因为Map要更新数据需要些定时器。而loadingCache不需要写,它内部就有过期处理的方法。同时LoadingCache内部采用的也是ConcurrentHashMap

dubbo缓存实现原理 dubbo 缓存_dubbo缓存实现原理_03

dubbo缓存实现原理 dubbo 缓存_List_04