背景
想起前两年工作中因为二级缓存默认开启导致的问题,完整的看了一个介绍 MyBatis 缓存机制的视频《MyBatis 缓存基础知识讲解》。
总计知识点:
- 缓存的类型及开关
- 这是个形同虚设的功能,线上环境应该禁用缓存
MyBatis 缓存分类
MyBasit 缓存类型如下:
- 一级缓存:SqlSession 级别的缓存,每个 SqlSession 对象维护一个缓存集合,多个 SqlSession 对象之间缓存不共享。
- 二级缓存:Mapper 级别的缓存,多个 SqlSession 访问相同 Mapper 对象的时候,共享该 Mapper 的缓存。
- 项目中的 SqlSession 是单例,还是多例?
一级缓存原理
- SQL 缓存的结构:sql ID + 参数,还是 SQL 语句?
- 缓存的结果类型:直接是游标,还是 Java 结果集合?
- 缓存的刷新,执行 增/删/该 方法之后都会刷新缓存。
二级缓存原理
相关知识:
1、二级缓存的数据存储介质多种,所以实体类需要实现序列化接口。
2、二级缓存与 SqlSession 无关。
3、MyBatisPlus 二级缓存默认是开启的
Mybatis 禁用缓存配置
首先,MyBatis 没有提供全局的一级缓存的关闭配置,需要在每个 Mapper 的 sql 语句中配置 flushCache=true
禁用一级缓存,例如:
<select id="getUserInfo" flushCache="true" parameterType="int" resultType="xx.UserInfo" >
select xx
</select>
其次,MyBatis-Plus 提供了二级缓存的开关配置 cache-enabled
,默认开启:
mybatis-plus:
mapper-locations: classpath*:/mapper/${spring.datasource.type}/*.xml,classpath*:/mapper/public/*.xml
#实体扫描,多个package用逗号或者分号分隔
type-aliases-package: com.xx.*.entity
configuration:
# 开启驼峰命名规则自动转换
map-underscore-to-camel-case: true
#注意:单引号,解决 Oracle参数为null时1111异常
jdbc-type-for-null: 'null'
# 本模块依赖其他模块操作数据库的数据,所以禁止缓存
cache-enabled: false
以前参与的一个项目中,有两个模块是相互依赖的,一个模块负责数据管理,另一个模块查询它的数据,使用数据库操作框架是 MyBatisPlus。
出现过一个问题,就是另一个模块修改了数据后,另一个模块监控到数据没有发生变化,后来想到是框架缓存的问题,cache-enabled: false
加上该配置就正确了。
总结
线上环境,应该关闭 MyBatis 的缓存:一级缓存在分布式环境中,容易导致脏数据;二级缓存是基于 namespace 的,对于涉及到多表关联的语句时,容易因为脏数据而得不到正确数据。
既然这个功能这么鸡肋,为什么当初要设计它呢?