1、前言
排查生产问题,报错接口中的同一条sql执行时间不同。
2、一级缓存
一级缓存是默认开启的,在一个sqlSession会话里面的所有 查询 操作都会保存到缓存中。如果同一个sqlSession会话中 2个查询中间有一个 insert 、update或delete 语句,那么之前查询的所有缓存都会清空;
2.1、一级缓存的工作原理
用户发起查询请求,查找某条数据,sqlSession 先去缓存中查找,是否有该数据,如果有,读取; 如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。
但 sqlSession 执行commit,即增删改操作时会清空缓存。这么做的目的是避免脏读。
2.2、一级缓存生效条件
一级缓存要生效,必须满足以下条件条件:
- 必须是相同的会话
- 必须是同一个 mapper,即同一个 namespace
- 必须是相同的 statement,即同一个 mapper 中的同一个方法
- 必须是相同的 SQL 和参数
- 查询语句中间没有执行
sqlSession.close()
方法 - 查询语句中间没有执行 insert/update/delete 方法(无论变动记录是否与缓存数据有无关系)
3、二级缓存
二级缓存是 SqlSessionFactory 级别,通过 SqlSessionFactory 创建SqlSession 查询的结果会被缓存。 此后若再次执行相同的查询,结果就会从缓存中获取。
3.1、二级缓存的流程图
- 开启二级缓存后,用户查询时,会先去二级缓存中找,找不到了再去一级缓存中找;
- 一级缓存也没有查询到,则查询数据库;
- 当sqlSession会话提交或者关闭时,一级缓存的数据会刷新到二级缓存中;
3.2 二级缓存生效条件
- 当会话提交或关闭之后才会填充二级缓存
- 必须是同一个 mapper,即同一个命名空间
- 必须是相同的 statement,即同一个 mapper 中的同一个方法
- 必须是相同的 SQL 语句和参数
- 如果 readWrite=true(默认就是true),实体对像必须实现 Serializable 接口
3.3、开启二级缓存
mybatis-plus.configuration.cache-enabled = true
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"> </cache>
<cache eviction="FIFO" // 回收策略为先进先出
flushInterval="60000" // 自动刷新时间60s
size="512" // 最多缓存512个引用对象
readOnly="true"/> // 只读
- POJO 类实现 Serializable 接口
4、一、二级缓存总结
- 二级缓存是 mapper 级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存的作用范围更大。
- 实际开发中,MyBatis 通常和 Spring 进行整合开发。
Spring 将事务放到 Service 中管理,对于每一个 service 中的 sqlsession 是不同的,
这是通过
mybatis-spring
中的org.mybatis.spring.mapper.MapperScannerConfigurer
创建 sqlsession 自动注入到 service 中的。 每次查询之后都要进行关闭 sqlSession ,关闭之后数据被清空。 所以 spring 整合之后,如果没有事务,一级缓存 是没有意义的。
5、使用原则(避坑)
- 只能在一个命名空间下使用二级缓存 由于二级缓存中的数据是基于namespace的,即不同 namespace 中的数据互不干扰。 在多个namespace中存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。
- 在单表上使用二级缓存 如果一个表与其它表有关联关系,那么就非常有可能存在多个 namespace 对同一数据的操作。 而不同 namespace 中的数据相互干扰,所以就有可能出现多个 namespace 中的数据不一致现象。
- 查询多于修改时使用二级缓存 在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。