MyBatis 缓存基本概念:
- 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
- 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
- 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
二级缓存补充说明
- 映射语句文件中的所有select语句将会被缓存。
- 映射语句文件中的所有insert,update和delete语句会刷新缓存。
- 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
- 缓存会根据指定的时间间隔来刷新。
- 缓存会存储1024个对象
二级缓存的使用
cache标签常用属性:
<cache
eviction="FIFO" <!--回收策略为先进先出-->
flushInterval="60000" <!--自动刷新时间60s-->
size="512" <!--最多缓存512个引用对象-->
readOnly="true"/> <!--只读-->
区别:
mybatis的一级缓存默认开启,且无法关闭。
究其根本,缓存的实际作用就是提高查询速度。
一级缓存与二级缓存的区别是 作用区间不同:
一级缓存作用于一次连接(一个sqlSession),在当前连接flush或close或执行C/U/D操作后一级缓存clear,
二级缓存与应用生命周期同步,其作用区间是命名空间Namespace,即每个mapper,在当前mapper执行C/U/D后二级缓存clear。
实际使用:
一级缓存:
强制开启,没啥好说的,优化查询速度。
二级缓存:
当频繁查询时二级缓存会表现优秀些,因为每次C/U/D都会刷缓存,反而大大降低系统性能。
所以应用场景是 访问的查询多 且 对查询结果的实时性要求不高 ==》 采用二级缓存降低数据库访问量,提高访问速度 【业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。】
实现方法如下:
通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
二级缓存的坑:
1、mybatis二级缓存对细粒度的数据级别的缓存实现不好。 — 小问题
比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。
2、代码编写规范 — 很重要
因为二级缓存的作用域是 命名空间,所以所有关于某个表的 CUD(增删改)操作必须都在同一个命名空间中,否则当其他Mapper修改了数据时,并不会刷新主Mapper的二级缓存,导致查询的数据是旧数据(脏读)。
如:Mapper A、Mapper B分别代表A、B表,当B中越界update了A表的一条数据,A的二级缓存并不会刷新,此时会发生脏读导致重大事故。
3、多表操作不能用二级缓存 — 很重要
沿用上面的例子,当B中关联查询了表A的数据且缓存了,那么A中CUD都不会刷到B的缓存,导致B的关联查询缓存结果都是旧的,脏读。
二级缓存作用域是 命名空间(Namespace) 不是表
缓存不支持 存储过程 为null
二级缓存缺陷解决方案:
• Mybatis拦截器 : 拦截SQL判断当是CUD时,将被执行的表的所有相关缓存手动清一下,但这样其实失去了缓存的意义。
• 放弃二级缓存,在业务层使用可控的缓存。集成Redis缓存