MyBatis一二级缓存

Mybatis 一级缓存,默认为 SESSION。

  • SESSION:Session 级别缓存,同一个 Session 相同查询语句不会再次查询数据库
  • STATEMENT:关闭一级缓存

在单服务架构中(仅有一个程序提供相同服务),开启一级缓存不会影响业务,只会提高性能。在微服务架构中需要关闭一级缓存,原因是:Service1 查询数据后,如果 Service2 修改了数据,Service1 再次查询时可能会得到过期数据。

MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。

MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。

在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。

建议MyBatis缓存特性在生产环境中进行关闭,单纯作为一个ORM框架使用可能更为合适。

测试一级缓存

1
2
3
4
5
6
7
8
9
10
11
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);

// 第一次查询(访问数据库)
User user1 = mapper.selectById(1);

// 第二次查询(命中一级缓存,不访问数据库)
User user2 = mapper.selectById(1);

System.out.println(user1 == user2); // 输出 true(同一对象引用)
}

注意:在SpringBoot中,以下使用方式可能返回false。

1
2
3
4
5
6
@Test
void test() {
User user1 = userMapper.selectById(1); // 查数据库
User user2 = userMapper.selectById(1); // 再次查数据库(缓存已失效)
System.out.println(user1 == user2); // 输出 false
}

原因:未启用事务。默认情况下,Spring 中每次数据库操作可能使用不同的 SqlSession,导致一级缓存失效。若测试方法未添加 @Transactional,两次 selectById 可能使用了不同的会话。

如两次查询中间插入更新操作,也会使缓存失效。

配置关闭一级缓存

通过设置 localCacheScopeSTATEMENT,使每次查询后自动清空缓存。

1
2
3
mybatis:
configuration:
local-cache-scope: STATEMENT # 关键配置

聊聊MyBatis缓存机制

MyBatis-Plus使用配置