Kevin's blog Kevin's blog
首页
  • AI基础
  • RAG技术
  • 提示词工程
  • Wireshark抓包
  • 常见问题
  • 数据库
  • 代码技巧
  • 浏览器
  • 手册教程
  • 技术应用
  • 流程规范
  • github技巧
  • git笔记
  • vpn笔记
  • 知识概念
  • 学习笔记
  • 环境搭建
  • linux&运维
  • 微服务
  • 经验技巧
  • 实用手册
  • arthas常用
  • spring应用
  • javaAgent技术
  • 网站
友情链接
  • 分类
  • 标签
  • 归档

Kevin

你可以迷茫,但不可以虚度
首页
  • AI基础
  • RAG技术
  • 提示词工程
  • Wireshark抓包
  • 常见问题
  • 数据库
  • 代码技巧
  • 浏览器
  • 手册教程
  • 技术应用
  • 流程规范
  • github技巧
  • git笔记
  • vpn笔记
  • 知识概念
  • 学习笔记
  • 环境搭建
  • linux&运维
  • 微服务
  • 经验技巧
  • 实用手册
  • arthas常用
  • spring应用
  • javaAgent技术
  • 网站
友情链接
  • 分类
  • 标签
  • 归档
  • 常见问题

    • 常见面试题
    • Mybatis一级缓存问题
      • 问题描述
      • 排查过程
      • 问题分析
      • 解决方案
  • 数据库

  • 代码技巧

  • 浏览器

  • spring应用

  • 使用Java Agent字节码技术扩展
  • 什么是AP,什么是CP,什么是CAP
  • RabbitMq相关
  • ELK查询技巧
  • 性能优化手段
  • 经验技巧
  • 常见问题
kevin
2024-10-28
目录

Mybatis一级缓存问题

# MyBatis 一级缓存导致数据不一致问题的排查与解决

# 问题描述

在项目中,我们需要循环处理数据库中的数据,核心流程如下:

  1. 查询未处理的数据,条件是 convertStatus = false。
  2. 处理查询到的数据,包括转换和保存。
  3. 更新处理过的数据的 convertStatus 为 true,以避免重复处理。

然而,在实际运行中,发现程序总是处理同一批数据,即第一次查询到的那批数据,导致数据处理陷入死循环,无法继续处理后续的数据。


# 排查过程

  1. 检查分页逻辑
    • 确认分页参数是否正确更新:确保在循环处理中,页码正确递增,没有始终查询第一页的数据。
  2. 验证 convertStatus 的更新
    • 在处理后,查询数据库:发现被处理的记录的 convertStatus 已经更新为 true,说明更新操作生效了。
    • 但是,程序仍然重复处理这些数据:即使数据库中 convertStatus 已经更新,查询结果仍然包含这些已处理的数据。
  3. 分析事务和缓存机制
    • 事务的影响:确认查询和更新操作在同一个事务中,根据事务的特性,事务内的更新应该对后续的查询可见(读自己的写)。
    • 怀疑 MyBatis 一级缓存导致问题:由于 MyBatis 的一级缓存会缓存同一 SqlSession 中的查询结果,可能导致查询操作返回缓存的旧数据。

# 问题分析

  • 事务内的数据可见性
    • 在同一个事务中,事务能够读取到自己未提交的更改,即“读自己的写”。
    • 因此,事务隔离级别并非问题的根源。
  • MyBatis 一级缓存的影响
    • 一级缓存作用于 SqlSession 级别:在同一个 SqlSession 中,相同的查询条件,MyBatis 会缓存查询结果。
    • 缓存未刷新:在更新操作后,如果未清除缓存,后续的查询操作会返回缓存中的旧数据。
    • 导致的问题:尽管数据库中的数据已更新,查询操作仍然返回缓存的旧数据,导致重复处理相同的数据。

# 解决方案

  1. 在更新操作后,手动清除 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
  2. 在查询方法上设置刷新缓存

    • 在 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
  3. 调整 SqlSession 的使用范围

    • 将查询和更新操作放在不同的 SqlSession 中:使得查询操作不会受到更新操作的缓存影响。
  4. 避免在同一个事务中处理过多数据

    • 分批处理数据:在处理完一批数据后,提交事务并开启新的事务和 SqlSession。

验证结果

  • 问题得到解决
    • 程序能够正确处理不同的数据批次:每次查询都能获取到最新的未处理数据。
    • 数据处理正常进行:数据库中 convertStatus = false 的记录数量逐渐减少,直至全部处理完毕。

思考与总结

  • MyBatis 一级缓存的影响
    • 缓存机制的特点:一级缓存提高了查询效率,但可能在更新数据后导致数据不一致的问题。
    • 在同一 SqlSession 中,查询操作可能返回缓存的旧数据,需要手动清除缓存或调整缓存策略。
  • 事务与缓存的关系
    • 事务内的更新对后续查询可见:在同一个事务中,事务能够读取到自己未提交的更改。
    • 但缓存可能干扰数据可见性:MyBatis 的一级缓存可能导致查询操作无法获取到最新的更新。
  • 解决问题的关键
    • 正确管理缓存:在需要获取最新数据时,手动清除缓存或设置刷新缓存的策略。
    • 理解框架机制:深入理解 MyBatis 和事务的工作原理,才能有效解决问题。

经验教训

  • 关注缓存机制带来的影响
    • 缓存可能导致数据不一致:在使用缓存的情况下,需要考虑缓存带来的数据一致性问题。
    • 及时刷新或清除缓存:在更新操作后,确保缓存中的数据也是最新的。
  • 深入理解框架的特性
    • MyBatis 的一级缓存不可全局禁用:需要根据具体情况,手动管理缓存。
    • 事务和缓存的交互:事务的提交和缓存的刷新都是影响数据可见性的因素。
上次更新: 2025/01/13, 14:57:37
常见面试题
Oracle技巧

← 常见面试题 Oracle技巧→

最近更新
01
AI是如何学习的
06-03
02
提示词工程实践指南
06-03
03
chatGpt提示原则
06-03
更多文章>
| Copyright © 2022-2025 Kevin | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式