首页 > 编程语言 >Mybatis源码4 Cache的实现和其原理

Mybatis源码4 Cache的实现和其原理

时间:2022-08-21 12:25:47浏览次数:49  
标签:缓存 lock Cache private 二级缓存 源码 key Mybatis

Mybatis源码4 Cache的实现和其原理

一丶Cache的实现类

image-20220314183803372

  • TransactionalCache 事务缓存,一次性存入多个缓存,移除多个缓存
  • PerpetualCache 基于HashMap的缓存实现
  • LoggingCache 提供打印命中率的功能
  • SynchronizedCache 同步缓存,防止多线程问题
  • LruCache LRU淘汰算法的缓存
  • SoftCache 软引用缓存
  • ScheduledCache 定时清除缓存
  • WeakCache 弱引用缓存
  • FifoCache 先进先出淘汰策略缓存
  • BlockingCache 阻塞缓存
  • SerializedCache 序列化缓存

二丶TransactionalCache

  //缓存事务真正存储的位置
  private Cache delegate;
  //commit时要不要清缓存
  private boolean clearOnCommit;
  //commit时要添加的元素
  private Map<Object, Object> entriesToAddOnCommit;
  //未命中的缓存key
  private Set<Object> entriesMissedInCache;
  • 提供了暂存区

    查询的数据暂时存放在entriesToAddOnCommit 而不是直接提交到二级缓存

    • entriesToAddOnCommit暂存区避免了脏读

    image-20220314183803372

    ​ 将要提交的数据暂存在暂存区(提交的时候将暂存区的数据存储到二级缓存,回滚则不影响),而不是立刻提交到二级缓存

    假如允许马上提交到二级缓存,回话回滚的时候无法回滚二级缓存中的数据,将导致另外的查询读到回滚掉的数据
    
    • entriesMissedInCache 可以避免缓存穿透么

      TransactionalCache 在提交的时候会把没有名字的key赋值对应null值,但是cachingExceutor查询的时候为null还是查询了数据库,如果为了防止缓存穿透,不应该赋值一个默认定义为空对象的值,然后查询的时候比较结果是否是空对象,如果是空返回空

    image-20220314192812738

    image-20220314192951019

    ​ 防止缓存穿透也存在弊端,那么就是另外一个服务新增数据到数据库中,数据库有数据了但是二级缓存没有,会造成错误

三丶LoggingCache

private Cache delegate;
//请求缓存次数
protected int requests = 0;
//命中次数
protected int hits = 0;

如果命中了hits++
查询缓存的时候打印命中率

如果是分布式缓存,岂不是只能打印当前机器的命中率,而且重启就归0了,只能说少即是多

四丶SynchronizedCache

image-20220314193441011

原理和Collections.synchronizedList()一样,利用synchronized保证线程安全,粒度很大

五丶LruCache

mybatis 实现LRU淘汰策略,是基于LinkedHashMap,构造LinkedHashMap的时候设置accessOrder为true重写其removeEldestEntry方法,在当前缓存大小大于指定缓存大小的时候返回true将会删除LinkedHashMap的头结点,从而删除最近最少使用缓存,且此为了删除真正缓存区中的数据,mybatis将要删除的key存储到自己的属性中,put的时候删除二级缓存中的数据

具体见 LinkedHashMap源码学习笔记

image-20220314195153134

image-20220314195227901

六丶SoftCache

//链表用来引用元素,防垃圾回收
private final Deque<Object> hardLinksToAvoidGarbageCollection;
//被垃圾回收的引用队列
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
//被装饰者
private final Cache delegate;
//强引用数量  配合 hardLinksToAvoidGarbageCollection 使用,指定数量的缓存将不被gc回收
private int numberOfHardLinks;

mybatis 使用 hardLinksToAvoidGarbageCollection 指向缓存对象从而保证缓存数据不被gc回收

使用queueOfGarbageCollectedEntries 在缓存对象被gc回收,将二级缓存的数据进行清除

  • 如何使用hardLinksToAvoidGarbageCollection 防止被回收

    image-20220314202428022

  • 如何使用queueOfGarbageCollectedEntries 在缓存对象被gc回收,将二级缓存的数据进行清除

    目标:gc回收缓存缓存中的数据的时候,我们可以删除二级缓存中的数据

    1. 我们要存储的数据应该是被软引用包装的
    2. 我们存储的数据被回收后任可以拿到原来数据对应的key,才可以去删除二级缓存中的数据
    3. 缓存数据要存在缓存队列中,这样才可以知道哪些被回收了

    image-20220314203031743

    加入我们使用的是PerpetualCache 作为二级缓存 PerpetualCache是基于hashmap这个时候hashMap存储了缓存内容那么SoftCache  还有效么
    
    有效,从GC root(可达性分析)
    加入缓存数据没有被方法栈Reference(没有任何变量执行缓存的值)那么缓存value的指向是
    hashMap----》SoftEntry(mybatis定义的继承了 SoftReference )----》缓存值
    对应缓存值来说只有SoftReference 指向它,gc自然能回收咯
    

    七丶WeakCache

    弱引用缓存,可以看到代码和SoftCache如出一辙,就是SoftReference变成了WeakReference
    

八丶ScheduledCache

​ 第一反应是不是mybatis启动了另外一个线程啊,每隔一段时间清理,结果

private Cache delegate;
//清理间隔
protected long clearInterval;
//上一次清理时间
protected long lastClear;

image-20220314203852321

​ mybatis的定时清理是每隔一段时间进行清理,依赖于用户要使用到二级缓存,也对,不用二级缓存 那么怎么可能存在数据过多oom呢 (啥叫少即是多啊 战术后仰)

九丶FifoCache

这个类就是维护一个FIFO队列,其他都委托给所包装的cache去做。

  private final Cache delegate;
  private Deque<Object> keyList;
  private int size;

image-20220314204151556

十丶SerializedCache

提供缓存的序列化,可以将缓存的数据转化二进制存入已经从二进制转化为对象,默认是使用SerializedCache装饰的(readWrite默认是true,为true那么将使用SerializedCache再次装饰)

image-20220314205201002

 ````

为什么要使用SerializedCache装饰,其实根据 readWrite 的名字就可以知道,readWrite可以让"读写分离",即读取对象改变对象的属性不会影响到二级缓存,用过是反序列化生成的对象,深拷贝是不同的内存区域。
````

十一丶BlockingCache

为了达到阻塞的目的(当前线程没从缓存获取到key对应的值,那么后续此key对应的缓存将无法被操作)

blockingcache

//锁超时时长
private long timeout;
//被装饰者
private final Cache delegate;
//key:缓存的key value :该缓存对应的锁
private final ConcurrentHashMap<Object, ReentrantLock> locks;
private ReentrantLock getLockForKey(Object key) {
  ReentrantLock lock = new ReentrantLock();
  //缓存的key没对应的锁 那么就new 一个锁
  ReentrantLock previous = locks.putIfAbsent(key, lock);
  return previous == null ? lock : previous;
}

private void acquireLock(Object key) {
  Lock lock = getLockForKey(key);
  if (timeout > 0) {
    try {
      //指定时间 企图拿到锁 如果 拿不到抛出异常
      boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
      if (!acquired) {
        throw new CacheException("Couldn't get a lock in " + timeout + " for the key " +  key + " at the cache " + delegate.getId());  
      }
    } catch (InterruptedException e) {
      throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
    }
  } else {
      //上锁
    lock.lock();
  }
}

private void releaseLock(Object key) {
  ReentrantLock lock = locks.get(key);
  //锁被当前线程持有 释放锁
  if (lock.isHeldByCurrentThread()) {
    lock.unlock();
  }
}

image-20220314210415515

image-20220314210436384

标签:缓存,lock,Cache,private,二级缓存,源码,key,Mybatis
From: https://www.cnblogs.com/cuzzz/p/16609765.html

相关文章

  • Mybatis源码5 StatementHandler ,ParameterHandler
    Mybatis源码5StatementHandler,ParameterHandler一丶概述前面我们总结了SqlSession--->CachingExecutor--->BaseExector---->Excutor子类doQuery,doUpdate的执行流程,my......
  • Mybatis 源码6 结果集映射流程 ,mybatis插件实现原理和基于mybatis插件实现参数化类型T
    Mybatis源码6结果集映射流程,mybatis插件实现原理和基于mybatis插件实现参数化类型TypeHandler一丶前情回顾书接上回,下面是SimpleExecutor执行查询的主要逻辑prepa......
  • Mybatis源码1JDBC->mybatis主要流程->mybatis Excutor简介
    Mybatis源码1JDBC->mybatis主要流程->mybatisExcutor简介一丶mybatis概述MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎......
  • Spring源码学习笔记4——BeanFactoryPostProcessor执行
    一丶BeanFactoryPostProcessor是什么Spring留给我们的一个扩展接口,在BeanDefinition加载注册完之后,并执行一些前置操作(笔记3)之后会反射生产所有的BeanFactoryPostProcesso......
  • Spring源码学习笔记6——Spring bean的实例化
    一丶前言前面我们了解到读取xmlor根据扫描路径生成BeanDefinition并注册到BeanFactory,相当于我们具备了生火做饭的原材料:BeanDefinition,接下来就是Spring最为核心的,根据......
  • Tomcat源码分析--类加载器
    Tomcat类加载器结构上图是Tomcat文档中所展示的Tomcat类加载结构。在这个结构中Bootstartap和System的类加载器由java虚拟机实现。common类加载器由Tomcat容器实现,它对......
  • Mybatis的一级、二级缓存
    一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Sessionflush或close之后,该Session中的所有Cache就将清空,默认打开一级缓存。二级......
  • 大家都能看得懂的源码 - ahooks useSet 和 useMap
    本文是深入浅出ahooks源码系列文章的第十篇,该系列已整理成文档-地址。觉得还不错,给个 star 支持一下哈,Thanks。今天我们来聊聊ahooks中对Map和Set类型进行状态......
  • Mybatis的缓存
    1.Mybatis的一级缓存Mybatis的一级缓存是默认开启的,你只要搭建一个Mybatis框架,就可以直接使用一级缓存。一级缓存是SqlSession级别的,通过SqlSession查询的数据会被缓存,......
  • Mybatis组件介绍
    核心组件SqlSessionFactoryBuilderSqlSessionFactoryBuilder的作用就是通过XML或者Java代码来建造一个工厂(SqlSessionFactory),并且可以通过它建造多个这样的工厂。一旦......