MyBatis缓存机制的设计与实现如何细粒度地控制你的MyBatis二级缓存(六)

本文如下组织结构:

一个关于MyBatis的二级缓存的实际问题
当前MyBatis二级缓存的工作机制
mybatis-enhanced-cache插件的设计和工作原理
mybatis-enhanced-cache 插件的使用实例

1.一个关于MyBatis的二级缓存的实际问题

现有AMapper.xml中定义了对数据库表ATable 的CRUD操作,BMapper定义了对数据库表BTable的CRUD操作;
假设 MyBatis 的二级缓存开启,并且 AMapper 中使用了二级缓存,AMapper对应的二级缓存为ACache;
除此之外,AMapper 中还定义了一个跟BTable有关的查询语句,类似如下所述:

<select id="selectATableWithJoin" resultMap="BaseResultMap" useCache="true">
      select * from ATable left join BTable on ....
</select>

执行以下操作:

  1. 执行AMapper中的"selectATableWithJoin" 操作,此时会将查询到的结果放置到AMapper对应的二级缓存ACache中;

  2. 执行BMapper中对BTable的更新操作(update、delete、insert)后,BTable的数据更新;

  3. 再执行1完全相同的查询,这时候会直接从AMapper二级缓存ACache中取值,将ACache中的值直接返回;
    好,问题就出现在第3步上:

由于AMapper的“selectATableWithJoin” 对应的SQL语句需要和BTable进行join查找,而在第 2 步BTable的数据已经更新了,但是第 3 步查询的值是第 1
步的缓存值,已经极有可能跟真实数据库结果不一样,即ACache中缓存数据过期了!

总结来看,就是

对于某些使用了 join连接的查询,如果其关联的表数据发生了更新,join连接的查询由于先前缓存的原因,导致查询结果和真实数据不同步;

从MyBatis的角度来看,这个问题可以这样表述:

对于某些表执行了更新(update、delete、insert)操作后,如何去清空跟这些表有关联的查询语句所造成的缓存;

当前的MyBatis的缓存机制不能很好地处理这一问题,下面我们将从当前的MyBatis的缓存机制入手,分析这一问题:

  1. 当前MyBatis二级缓存的工作机制

当前MyBatis二级缓存的工作机制:
在这里插入图片描述
MyBatis二级缓存的一个重要特点:即松散的Cache缓存管理和维护。

一个Mapper中定义的增删改查操作只能影响到自己关联的Cache对象。如上图所示的Mapper
namespace1中定义的若干CRUD语句,产生的缓存只会被放置到相应关联的Cache1中,即Mapper
namespace2,namespace3,namespace4 中的CRUD的语句不会影响到Cache1。

可以看出,Mapper之间的缓存关系比较松散,相互关联的程度比较弱。

现在再回到上面描述的问题,如果我们将AMapper和BMapper共用一个Cache对象,那么,当BMapper执行更新操作时,可以清空对应Cache中的所有的缓存数据,这样的话,数据不是也可以保持最新吗?

确实这个也是一种解决方案,不过,它会使缓存的使用效率变的很低!AMapper和BMapper的任意的更新操作都会将共用的Cache清空,会频繁地清空Cache,导致Cache实际的命中率和使用率就变得很低了,所以这种策略实际情况下是不可取的。

最理想的解决方案就是:
对于某些表执行了更新(update、delete、insert)操作后,如何去清空跟这些表有关联的查询语句所造成的缓存;

这样,就是以很细的粒度管理MyBatis内部的缓存,使得缓存的使用率和准确率都能大大地提升。
基于这个思路,我写了一个对应的mybatis-enhanced-cache 缓存插件,可以很好地支持上述的功能。
对于上述的例子中,该插件可以实现:当BMapper对BTable执行了更新操作时,指定清除与BTable相关联的selectATableWithJoin查询语句在ACache中产生的缓存。

接下来就来看看这个mybatis-enhanced-cache插件的设计原理吧:

3.mybatis-enhanced-cache插件的设计和工作原理

该插件主要由两个构件组成:EnhancedCachingExecutor和EnhancedCachingManager。

EnhancedCachingExecutor是针对于Executor的拦截器,拦截Executor的几个关键的方法;

EnhancedCachingExecutor主要做以下几件事:

  1. 每当有Executor执行query操作时,
    1.1 记录下该查询StatementId和CacheKey,然后将其添加到EnhancedCachingManager中;
    1.2 记录下该查询StatementId 和此StatementId所属Mapper内的Cache缓存对象引用,添加到EnhancedCachingManager中;
  2. 每当Executor执行了update操作时,将此 update操作的StatementId传递给EnhancedCachingManager,让EnhancedCachingManager根据此update的StatementId的配置,去清空指定的查询语句所产生的缓存;

注:我们只需要看思路,这里类似于创建了一个监听器或者观察者,当Excutor进行查询就执行操作将信息存到自己内部,执行增删改就去清理缓存

另一个构件:EnhancedCachingManager,它也是本插件的核心,它维护着以下几样东西:

  1. 整个MyBatis的所有查询所产生的CacheKey集合(以statementId分类);

  2. 所有的使用过了的查询的statementId 及其对应的Cache缓存对象的引用;

  3. update类型的StatementId和查询StatementId集合的映射,用于当Update类型的语句执行时,根据此映射决定应该清空哪些查询语句产生的缓存;

注:我们只需要看思路,这里类似于创建了一个监听器或者观察者,当Excutor进行查询就执行操作将信息存到自己内部,执行增删改就去清理缓存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值