MyBatis(3)二级缓存源码解析

MyBatis二级缓存源码解析

文本主要目的为掌握二级缓存的使用场景、熟悉其执行结构、以及执行过程源码

二级缓存概述

二级缓存也称作是应用级缓存,与一级缓存不同的,是它的作用范围是整个应用,而且可以跨线程使用。所以二级缓存有更高的命中率,适合缓存一些修改较少的数据。在流程上是先访问二级缓存,在访问一级缓存。

二缓存需求

二级缓存是一个完整的缓存解决方案,那应该包含哪些功能呢?这里我们分为核心功能和非核心功能两类:

存储【核心功能】

即缓存数据库存储在哪里?常用的方案如下:

  1. 内存:最简单就是在内存当中,不仅实现简单,而且速度快。内存弊端就是不能持久化,且容易有限。

  2. 硬盘:可以持久化,容量大。但访问速度不如内存,一般会结合内存一起使用。

  3. 第三方集成:在分布式情况,如果想和其它节点共享缓存,只能第三方软件进行集成。比如Redis. 

溢出淘汰【核心功能】

无论哪种存储都必须有一个容易,当容量满的时候就要进行清除,清除的算法即溢出淘汰机制。常见算法如下:

  1. FIFO:先进先出

  2. LRU:最近最少使用

  3. WeakReference: 弱引用,将缓存对象进行弱引用包装,当Java进行gc的时候,不论当前的内存空间是否足够,这个对象都会被回收 

  4. SoftReference:软件引用,基机与弱引用类似,不同在于只有当空间不足时GC才才回收软引用对象。

其它功能

  1. 过期清理:指清理存放数据过久的数据

  2. 线程安全:保证缓存可以被多个线程同时使用

  3. 写安全:当拿到缓存数据后,可对其进行修改,而不影响原本的缓存数据。通常采取做法是对缓存对象进行深拷贝。

二级缓存责任链设计

这么多的功能,如何才能简单的实现,并保证它的灵活性与扩展性呢?这里MyBatis抽像出Cache接口,其只定义了缓存中最基本的功能方法:

  • 设置缓存

  • 获取缓存

  • 清除缓存

  • 获取缓存数量

然后上述中每一个功能都会对应一个组件类,并基于装饰者加责任链的模式,将各个组件进行串联。在执行缓存的基本功能时,其它的缓存逻辑会沿着这个责任链依次往下传递。

这样设计有以下优点:

  1. 职责单一:各个节点只负责自己的逻辑,不需要关心其它节点。

  2. 扩展性强:可根据需要扩展节点、删除节点,还可以调换顺序保证灵活性。

  3. 松耦合:各节点之间不没强制依赖其它节点。而是通过顶层的Cache接口进行间接依赖。

二级缓存的使用

缓存空间声明

二级默认缓存默认是不开启的,需要为其声明缓存空间才可以使用,通过@CacheNamespace 或 为指定的MappedStatement声明。声明之后该缓存为该Mapper所独有,其它Mapper不能访问。如需要多个Mapper共享一个缓存空间可通过@CacheNamespaceRef 或进行引用同一个缓存空间。@CacheNamespace 详细配置见下表:

配置

说明

implementation

指定缓存的存储实现类,默认是用HashMap存储在内存当中

eviction

指定缓存溢出淘汰实现类,默认LRU ,清除最少使用

flushInterval

设置缓存定时全部清空时间,默认不清空。

size

指定缓存容量,超出后就会按eviction指定算法进行淘汰

readWrite

true即通过序列化复制,来保证缓存对象是可读写的,默认true

blocking

为每个Key的访问添加阻塞锁,防止缓存击穿

properties

为上述组件,配置额外参数,key对应组件中的字段名。

 

 

注:Cache中责任链条的组成即通过@CacheNamespace 指导生成。具体逻辑详兔崽子CacheBuilder

缓存其它配置

除@CacheNamespace 还可以通过其它参数来控制二缓存

字段

配置域

说明

cacheEnabled

 

二级缓存全局开关,默认开启

useCache

<select|update|insert|delete>

指定的statement是否开启,默认开启

flushCache

<select|update|insert|delete>

执行sql前是否清空当前二级缓存空间,update默认true。query默认false

二级缓存的命中条件

二级缓存的命中场景与一级缓存类似,不同在于二级可以跨会放使用,还有就是二级缓存的更新,必须是在会话提交之后。

为什么要提交之后才能命中缓存?

如上图两个会话在修改同一数据,当会话二修改后,在将其查询出来,假如它实时填充到二级缓存,而会话一就能过缓存获取修改之后的数据,但实质是修改的数据回滚了,并没真正的提交到数据库。

所以为了保证数据一至性,二级缓存必须是会话提交之才会真正填充,包括对缓存的清空,也必须是会话正常提交之后才生效。

二级缓存结构

为了实现会话提交之后才变更二级缓存,MyBatis为每个会话设立了若干个暂存区,当前会话对指定缓存空间的变更,都存放在对应的暂存区,当会话提交之后才会提交到每个暂存区对应的缓存空间。为了统一管理这些暂存区,每个会话都一个唯一的事物缓存管理 器。所以这里暂存区也可叫做事物缓存。

最后我们通过下图来了解会话、暂存区、二级缓存空间的关系:

二级缓存执行流程

原本会话是通过Executor实现SQL调用,这里基于装饰器模式使用CachingExecutor对SQL调用逻辑进行拦截。以嵌入二级缓存相关逻辑。

查询操作query

当会话调用query() 时,会基于查询语句、参数等数据组成缓存Key,然后尝试从二级缓存中读取数据。读到就直接返回,没有就调用被装饰的Executor去查询数据库,然后在填充至对应的暂存区。

请注意,这里的查询是实时从缓存空间读取的,而变更,只会记录在暂存区

更新操作update

当执行update操作时,同样会基于查询的语句和参数组成缓存KEY,然后在执行update之前清空缓存。这里清空只针对暂存区,同时记录清空的标记,以便当会话提交之时,依据该标记去清空二级缓存空间。

如果在查询操作中配置了flushCache=true ,也会执行相同的操作。

提交操作commit

当会话执行commit操作后,会将该会话下所有暂存区的变更,更新到对应二级缓存空间去。

06-02 153
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值