Mybatis二级缓存详解

目录

二级缓存

MyBatis的缓存机制整体设计以及二级缓存的工作模式

二级缓存的划分

使用二级缓存,必须要具备的条件

一级缓存和二级缓存的使用顺序

二级缓存实现的选择

MyBatis自身提供的二级缓存的实现

二级缓存的作用

二级缓存的作用域

二级缓存应用场景

二级缓存局限性

二级缓存的思考


  • 二级缓存

  • MyBatis的二级缓存是Application应用级别的缓存
  • 多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的
  • 二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache
  • MyBatis的缓存机制整体设计以及二级缓存的工作模式

  • 当开一个会话时,一个SqlSession对象会使用一个Executor对象来完成会话操作,MyBatis的二级缓存机制的关键就是对这个Executor对象做文章
  • 如果用户配置了"cacheEnabled=true",那么MyBatis在为SqlSession对象创建Executor对象时,会对Executor对象加上一个装饰者:CachingExecutor
  • 这时SqlSession使用CachingExecutor对象来完成操作请求
  • CachingExecutor对于查询请求,会先判断该查询请求在Application级别的二级缓存中是否有缓存结果
  • 如果有查询结果,则直接返回缓存结果
  • 如果缓存中没有,再交给真正的Executor对象来完成查询操作
  • 之后CachingExecutor会将真正的Executor返回的查询结果放置到缓存中,然后再返回给用户
  • CachingExecutor是Executor的装饰者,以增强Executor的功能,使其具有缓存查询的功能,这里用到了设计模式中的装饰者模式
  • 二级缓存的划分

  • MyBatis并不是简单地对整个Application就只有一个Cache缓存对象
  • 它将缓存划分的更细,即是Mapper级别的,即每一个Mapper都可以拥有一个Cache对象,具体如下:
    • (1)为每一个Mapper分配一个Cache缓存对象(使用<cache>节点配置)
      • MyBatis将Application级别的二级缓存细分到Mapper级别
      • 即对于每一个Mapper.xml,如果在其中使用了<cache>节点,则MyBatis会为这个Mapper创建一个Cache缓存对象
      • 例:对于namespace1和namespace2两个Mapper,都配置了<cache>会分别为这两个Mapper根据配置信息创建一个Cache对象
      • 而namespace3的Mapper,没有配置<cache>节点,则对于Mapper namespace3就没有对应的二级缓存
      • 上述的每一个Cache对象,都会有一个自己所属的namespace命名空间,并且会将Mapper的namespace作为它们的ID
    • (2)多个Mapper共用一个Cache缓存对象(使用<cache-ref>节点配置)
      • 如果你想让多个Mapper公用一个Cache的话,你可以使用<cache-ref namespace="">节点,来指定这个Mapper使用到了哪一个Mapper的Cache缓存
      • 例:Mapper namespace2 的<cache-ref>定义的namespace="namespace1",说明Mapper namespace2 将使用Mapper namespace1中的Cache缓存对象,这时候要求namespace1中一定要有<cache>节点的定义
  • 使用二级缓存,必须要具备的条件

  • MyBatis对二级缓存的支持粒度很细,它会指定某一条查询语句是否使用二级缓存
  • 虽然在Mapper中配置了<cache>,并且为此Mapper分配了Cache对象,但这并不表示我们使用Mapper中定义的查询语句查到的结果都会放置到Cache对象之中
  • 我们必须指定Mapper中的某条选择语句是否支持缓存,即如下所示:
  • 在<select>节点中配置useCache="true",Mapper才会对此Select的查询支持缓存特性,否则,对此Select查询不会经过Cache缓存
  • 如下所示,Select语句配置了useCache="true",则表明这条Select语句的查询会使用二级缓存

  • 总之,要想使某条Select查询支持二级缓存,你需要保证:
    • 1. MyBatis支持二级缓存的总开关:全局配置变量参数 cacheEnabled=true
    • 2. 该select语句所在的Mapper,配置了<cache>或<cached-ref>节点,并且有效
    • 3. 该select语句的参数 useCache=true
  • 一级缓存和二级缓存的使用顺序

  • 请注意,如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存
  • 那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存
  • 即MyBatis查询数据的顺序是:二级缓存——> 一级缓存——> 数据库
  • 二级缓存实现的选择

  • MyBatis对二级缓存的设计非常灵活,它自己内部实现了一系列的Cache缓存实现类,并提供了各种缓存刷新策略如LRU,FIFO等等
  • 另外,MyBatis还允许用户自定义Cache接口实现,用户是需要实现org.apache.ibatis.cache.Cache接口,然后将Cache实现类配置在<cache type="">节点的type属性上即可
  • 除此之外,MyBatis还支持跟第三方内存缓存库如Memecached的集成
  • 总之,使用MyBatis的二级缓存有三个选择:
    • 1.MyBatis自身提供的缓存实现
    • 2.用户自定义的Cache接口实现
    • 3.跟第三方内存缓存库的集成
  • MyBatis自身提供的二级缓存的实现

  • MyBatis自身提供了丰富的,并且功能强大的二级缓存的实现,它拥有一系列的Cache接口装饰者,可以满足各种对缓存操作和更新的策略
  • MyBatis定义了大量的Cache的装饰器来增强Cache缓存的功能,如下类图所示

  • 对于每个Cache而言,都有一个容量限制,MyBatis提供了各种策略来对Cache缓存的容量进行控制,以及对Cache中的数据进行刷新和置换
  • MyBatis主要提供了以下几个刷新和置换策略:
    • LRU(Least Recently Used):最近最少使用算法,即如果缓存中容量已经满了,会将缓存中最近做少被使用的缓存记录清除掉,然后添加新的记录
    • FIFO(First in first out):先进先出算法,如果缓存中的容量已经满了,那么会将最先进入缓存中的数据清除掉
    • Scheduled:指定时间间隔清空算法,该算法会以指定的某一个时间间隔将Cache缓存中的数据清空
  • 二级缓存的作用

  • 1.映射语句文件中的所有select语句将会被缓存
  • 2.映射语句文件中的所有insert、update和delete语句会刷新缓存
  • 3.缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来清除不需要的缓存
  • 4.缓存不会定时进行刷新(也就是说,没有刷新间隔)
  • 5.缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
  • 6.缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改
  • 二级缓存的作用域

  • 缓存只作用于 cache 标签所在的映射文件中的语句
  • 如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存
  • 你需要使用 @CacheNamespaceRef 注解指定缓存作用域
  • 二级缓存应用场景

  • 对查询频率高,变化频率低的数据建议使用二级缓存
  • 对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度
  • 业务场景比如:耗时较高的统计分析sql、电话账单查询sql等
  • 二级缓存局限性

  • mybatis二级缓存对细粒度的数据级别的缓存实现不好
  • 比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大
  • 但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息
  • 因为mybatis的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空
  • 解决此类问题需要在业务层根据需求对数据有针对性缓存
  • 二级缓存的思考

  • 1.MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强
  • 2.MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻
  • 3.在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高
  • 4.通常我们会为每个单表创建单独的映射文件,由于MyBatis的二级缓存是基于namespace的,多表查询语句所在的namspace无法感应到其他namespace中的语句对多表查询中涉及的表进行的修改,引发脏数据问题

猜你喜欢

转载自blog.csdn.net/weixin_59624686/article/details/130715630