MyBatis缓存模块分析

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/newbie0107/article/details/102734655

我们之前简单介绍过 MyBatis 缓存 ,这里我们来详细介绍一下我们 MyBatis 缓存的实现,这里主要是指 MyBatis 的一级缓存的实现。

我们在说缓存模块之前介绍过一个 装饰模式 ,因为我们MyBatis 的设计就运用到了我们的装饰模式。
在这里插入图片描述

这个 Cache 接口里面主要定义了一些缓存基本的方法,如获取一个缓存、放入一个缓存、移除一个缓存以及清除缓存等等。
在这里插入图片描述

我们刚刚也说了MyBatis的缓存模板是运用到了装饰模式,那么它肯定有一个基本的实现类,我们发现原来MyBatis的缓存都是基于Map来实现的,其Cache的方法其实也都是调用了Map的一些方法。
在这里插入图片描述
在这里插入图片描述

然后我们MyBatis还实现了很多的装饰类,分为拥有其不同的功能。
在这里插入图片描述


我们在 装饰模式 中也发现了,其实装饰类也就是对我们的基础的实现类进行了一些增强处理,不同的装饰类会对我们的类进行不同的增强处理,使其增加不同的能力。其处理过程都是对目标对象的一些方法进行增强,这里我们以 BlockingCache 为例。


BlockCache是一个阻塞版本的缓存装饰器,既然是堵塞版本,我们应该可以设置其堵塞的超时时间—— timeout,我们还应有锁的集合—— locks,另外我们装饰模式,肯定到时候我们需要将目标对象传入其中—— delegate
在这里插入图片描述

这里装饰类对其中的一些方法还是调用其原有的方法进行执行,这里我们主要查看其获取缓存和放入缓存方法是如何进行增强处理的。
在这里插入图片描述

我们的 BlockCache 在获取缓存的时候首先会进行加锁
在这里插入图片描述

我们在加锁的过程中,会先去锁的集合中去获取锁,若集合中之前没有这个对象的锁,我们就创建一个可重入锁,放入其中,否则我们就将这个对象之前的锁取出,最后将这个对象的锁返回进行加锁(这里分有无设置超时时间两种情况进行讨论)
在这里插入图片描述

这里获取锁之后,我们先去缓存区获取该对象的值,取到值之后我们就进行释放锁,这个过程就结束了。但是要是我们在缓存中没有获取到值,说明缓存中没有缓存该key的值,我们就需要到数据库中获取数据,并且我们还没有释放锁。
在这里插入图片描述
在这里插入图片描述

扫描二维码关注公众号,回复: 7622138 查看本文章



这里我们就来看看 BlockingCache 的放入缓存的方法。我们发现我们向缓存放入数据后,释放了锁,这个锁就是我们在获取数据时,未在缓存中获取到数据时加的锁,一直没有释放,等它去数据库中获取到了数据,放入缓存中之后,才释放的锁。
在这里插入图片描述


我们会发现我们在访问缓存和数据库时,为什么按照取值的key值进行加锁呢?因为我们都知道我在我们日常的工作中我们可能会给每个运行加上缓存以提高其性能,但是缓存也存在一个缓存雪崩的问题。


就是万一我们的应用刚刚运行,缓存没有任何数据,或者说是我们的缓存刚还失效,正准备刷新缓存时,这时又恰好遇到了高并发的访问,同一时间很多请求全部要连接数据库,那么我们数据库肯定就承受不住了,这里我们加上锁就可以很好地避免这个问题。


注意的是,上述是加得锁的粒度较细,使用key为单位的,就是同一key值的多次请求,只有一个请求能访问,其余被堵塞,所以我们用来一个集合来存储我们的锁,这样我们的应用性能不至于太差,但是如果我们访问较大,且key各不相同,我们还有可能会承受不住的。


但是呢,如果我们将锁粗化,如我们就用一把统一的锁来控制,同一时间只能有个请求可以访问, 这样的确可以绝对的保证其安全性,但是我们应用的性能又会极差。




刚刚我们说的情况下,缓存可能有过几秒刷新一次,这样的情况我们MyBatis也为我们设计好了,只要在包装一下ScheduleCache装饰类,我们的缓存就有具备了自动刷新的功能,如下
在这里插入图片描述



上述我们在说到MyBatis的缓存时,说其实底层也是利用了Map来进行实现的,那么它的key值是如何生成的呢?
我们这里说的缓存主要是指MyBatis的一级缓存,我们在 MyBatis 缓存 中说过
在这里插入图片描述

MyBatis 中 CachKey 的构造缓存 key 值的对象如下:

  • mappedStatment 的 id
  • 指定查询结果集的范围(分页信息)
  • 查询所使用的SQL语句
  • 用户传递给SQL语句的实际参数值

其中就是我们我们说的方法名和参数,因为 mappedStatment 的 id 相同,也就是值我们要使用的方法名相同,所以里面对应的SQL语句也相同,然后我们分页信息,实际参数值等相同,其实只要我们传入的参数相同,哪怕是动态SQL,最后也都是相同的。



我们就先来看看我们MyBatis缓存中的key
在这里插入图片描述

刚刚我们说了 MyBatis 中 CachKey 的构造缓存 key 值的对象,其中我们就会将那四个对象存入到 updateList 中去,
在这里插入图片描述

我们发现我们在每加入一个对象时,都会根据一些算法来计算一下,来计算出一些我们待会用于比较对象是否相同的变量值。
在这里插入图片描述

我们把对象的 CacheKey 计算好了之后,那么我们再来看看我们是否来比较其 CacheKey 值来判断是不是同一个,我们会发现其比较过程非常的严谨,先会比较是否为同一个对象,然后就会比较我们刚刚计算的一些值,就算全部一样之后,还会遍历比较updateList里的内容。
在这里插入图片描述


最后我们再来看看我们是如何查询的,我们在查询时,肯定要先生存 CacheKey 值,然后去查询,我们先查看下 BaseExecutor 类,这是一个访问数据库的类。
在这里插入图片描述

我们在生成 CacheKey 值的时候,其参数传入的顺序也需要一致的。
在这里插入图片描述

然后依据生成的 key 值,先去缓存中获取,没有的话再去数据库中获取。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/newbie0107/article/details/102734655