Mybatis一级缓存问题
# MyBatis 一级缓存导致数据不一致问题的排查与解决
# 问题描述
在项目中,我们需要循环处理数据库中的数据,核心流程如下:
- 查询未处理的数据,条件是
convertStatus = false
。 - 处理查询到的数据,包括转换和保存。
- 更新处理过的数据的
convertStatus
为true
,以避免重复处理。
然而,在实际运行中,发现程序总是处理同一批数据,即第一次查询到的那批数据,导致数据处理陷入死循环,无法继续处理后续的数据。
# 排查过程
- 检查分页逻辑
- 确认分页参数是否正确更新:确保在循环处理中,页码正确递增,没有始终查询第一页的数据。
- 验证
convertStatus
的更新- 在处理后,查询数据库:发现被处理的记录的
convertStatus
已经更新为true
,说明更新操作生效了。 - 但是,程序仍然重复处理这些数据:即使数据库中
convertStatus
已经更新,查询结果仍然包含这些已处理的数据。
- 在处理后,查询数据库:发现被处理的记录的
- 分析事务和缓存机制
- 事务的影响:确认查询和更新操作在同一个事务中,根据事务的特性,事务内的更新应该对后续的查询可见(读自己的写)。
- 怀疑 MyBatis 一级缓存导致问题:由于 MyBatis 的一级缓存会缓存同一 SqlSession 中的查询结果,可能导致查询操作返回缓存的旧数据。
# 问题分析
- 事务内的数据可见性
- 在同一个事务中,事务能够读取到自己未提交的更改,即“读自己的写”。
- 因此,事务隔离级别并非问题的根源。
- MyBatis 一级缓存的影响
- 一级缓存作用于 SqlSession 级别:在同一个 SqlSession 中,相同的查询条件,MyBatis 会缓存查询结果。
- 缓存未刷新:在更新操作后,如果未清除缓存,后续的查询操作会返回缓存中的旧数据。
- 导致的问题:尽管数据库中的数据已更新,查询操作仍然返回缓存的旧数据,导致重复处理相同的数据。
# 解决方案
在更新操作后,手动清除 MyBatis 一级缓存
调用
sqlSession.clearCache()
方法:清除当前 SqlSession 的缓存,确保后续查询能够获取最新数据。java 复制代码 @Autowired private SqlSessionTemplate sqlSessionTemplate; public void updateConvertStatus(List<String> ids) { // 执行更新操作 yourMapper.updateStatus(ids); // 清除一级缓存 sqlSessionTemplate.clearCache(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
在查询方法上设置刷新缓存
在 Mapper 方法上添加
@Options(flushCache = Options.FlushCachePolicy.TRUE)
注解:使得在执行查询时刷新缓存。java 复制代码 public interface YourMapper { @Options(flushCache = Options.FlushCachePolicy.TRUE) @Select("SELECT * FROM your_table WHERE convert_status = false") List<YourEntity> selectUnprocessedData(); }
1
2
3
4
5
6
7
8
9
10
调整 SqlSession 的使用范围
- 将查询和更新操作放在不同的 SqlSession 中:使得查询操作不会受到更新操作的缓存影响。
避免在同一个事务中处理过多数据
- 分批处理数据:在处理完一批数据后,提交事务并开启新的事务和 SqlSession。
验证结果
- 问题得到解决
- 程序能够正确处理不同的数据批次:每次查询都能获取到最新的未处理数据。
- 数据处理正常进行:数据库中
convertStatus = false
的记录数量逐渐减少,直至全部处理完毕。
思考与总结
- MyBatis 一级缓存的影响
- 缓存机制的特点:一级缓存提高了查询效率,但可能在更新数据后导致数据不一致的问题。
- 在同一 SqlSession 中,查询操作可能返回缓存的旧数据,需要手动清除缓存或调整缓存策略。
- 事务与缓存的关系
- 事务内的更新对后续查询可见:在同一个事务中,事务能够读取到自己未提交的更改。
- 但缓存可能干扰数据可见性:MyBatis 的一级缓存可能导致查询操作无法获取到最新的更新。
- 解决问题的关键
- 正确管理缓存:在需要获取最新数据时,手动清除缓存或设置刷新缓存的策略。
- 理解框架机制:深入理解 MyBatis 和事务的工作原理,才能有效解决问题。
经验教训
- 关注缓存机制带来的影响
- 缓存可能导致数据不一致:在使用缓存的情况下,需要考虑缓存带来的数据一致性问题。
- 及时刷新或清除缓存:在更新操作后,确保缓存中的数据也是最新的。
- 深入理解框架的特性
- MyBatis 的一级缓存不可全局禁用:需要根据具体情况,手动管理缓存。
- 事务和缓存的交互:事务的提交和缓存的刷新都是影响数据可见性的因素。
上次更新: 2025/01/13, 14:57:37