MyBatis功能点之一(2):二级缓存cache

  对于Mybatis缓存分作用域等维度区别一、二级缓存特点如下图:
            

  对于缓存的作用域,之前文章五、MyBatis缓存初体验 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中已经验证,sqlsesion.close()仅对一级缓存有影响,而update等对一/二级缓存均有影响。那从session为入口分析一级缓存,从mapper分析二级缓存。

  下面主要从底层数据结构这个维度理解一二级缓存。既然都是基于Perpetualcache的HashMap本地缓存,那么通过调试跟踪到其源码所在位置分析:

            

  PerpetualCache是对JUC中Cache接口的实现,其核心结构为命名为cache的Map<Object, Object>;所以缓存实际就是一个附带id的HashMap。实际上是不是只要实现了JUC中Cache接口均可以作为Mybatis备用缓存???当然可以,可参考MyBatis缓存Cache包 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)

  一级缓存

  跟踪调试仅使用一级缓存的代码,调试信息发现其对SimpleExecutor进行了包装——即SimpleExectutor或其上级父类中对一级缓存进行处理,跟踪调试信息,可得如下调用链(查看MyBatis中执行器Executor框架 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)):

        

  一级缓存功能由BaseExecutor类实现,即意味着每个实际执行器都具有这一级的功能:

        

   那么其具体应用逻辑为何?查看源码如下:

         

   从源码分析可得:如果设置了每次查询都清缓存,那么每次查询清缓存。同时查询之前判断localCache中是否有当前查询statement.id的相应数据,如果有则直接从一级缓存中获取结果,否则进行查询数据库的操作。

  二级缓存

    二级缓存的作用范围是一个命名空间(即一个映射文件或多个mapper文件同一个命名空间),而且可以实现多个命名空间共享一个缓存。跟踪二级缓存,其建立在mapper配置文件解析过程中即建立,而不是在opensession中。

        

     二级缓存默认开启,如取消采用如下配置:

        <settings>
         <setting name="cacheEnabled" value="false"/>
        </settings>
    需要注意的是开启并不表示生效使用,需进行如下操作:
      1)注解方式在mapper接口中使用如下注解,二级缓存生效,否则开启亦不生效。
      2)同理在mapper.xml文件中也需配置<cache></cache>表示二级缓存生效

      通过注解

        

    开启二级缓存,查看注解源码

           

    可知默认使用PerpetualCache的实现,同时通过implementation属性说明二级缓存是可配置的。可以配置用户需要使用的第三方或自定义缓存,比如redis。

    那二级缓存是如何工作的?我们开启二级缓存通过调试查看源码,如下:

          

     可知CachingExecutor中首先查询二级缓存,如果查询结果为空则业务逻辑有simpleExecutor处理即一级缓存的使用逻辑。

    开篇提到,sqlSession.close仅对一级缓存清空二级缓存无影响,以及update等操作对一二级缓存均清空,那么通过close和update源码分别来证实:

          

     分析CachingExecutor中close的业务逻辑:

          

     首先处理二级缓存rollback或者commit,再处理一级缓存赋值null——即close操作会清空一级缓存,二级缓存强制回退或者提交数据。

           

     分析CachingExecutor中update的业务逻辑,显然与close方法不同,如下:

          

     在上面分析过程中都遇到了CachingExecutor的一个属性,tcm——啥玩意?TransactionalCacheManager

          

     MyBatis将Cache,TransactionalCache做了映射,而不是和一级缓存保存一致,直接使用,这是为什么呢?下面附源码两张:

          

          

     第一张图说明一级缓存直接在executor中活动,即作用范围为session;第二张图说明二级缓存在MappedStatement中活动,作用范围跨session了。同样证实了开篇提到的cache作用范围。也因为二级缓存的作用范围跨session,可以同时被多个session同时获取就出现了线程安全的问题,导致脏读问题。为了解决这一问题,就有了TransactionalCache及其Manager——就是tcm对应类。

          

     从其类图中,可以直观的发现其增加了读写锁相关的功能。

 

posted on 2021-04-03 15:13  池塘里洗澡的鸭子  阅读(239)  评论(0编辑  收藏  举报