news 2026/7/11 8:08:16

聊聊 MyBatis 缓存的 “安全性”:为啥同一个 SqlSession 里改数据不会查到假数据?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
聊聊 MyBatis 缓存的 “安全性”:为啥同一个 SqlSession 里改数据不会查到假数据?
    • 先明确:两种“修改数据”的场景,结果完全不同
      • 场景1:同一个SqlSession内执行增删改操作(修改对应数据)
      • 场景2:外部修改了数据库数据(同一个SqlSession期间,其他SqlSession/应用改了数据)
    • 怎么解决“外部修改数据导致缓存不一致”的问题?
      • 1. 缩短SqlSession的生命周期(最常用)
      • 2. 手动清空一级缓存
      • 3. 对关键查询禁用一级缓存(不推荐,除非必要)
      • 4. 结合二级缓存+第三方分布式缓存(适合分布式系统)
    • 总结:MyBatis缓存的“安全性”是相对的

我最开始用MyBatis一级缓存的时候,也有过一模一样的顾虑——要是同一个SqlSession里,我先查了数据,然后别的地方改了数据库里的这条数据,我再查的时候会不会拿到缓存里的旧数据(假数据)?后来实际测试+看了MyBatis的源码逻辑,才发现这个担心其实分两种情况,MyBatis早就做了对应的处理,只是我们需要搞清楚场景差异。

先明确:两种“修改数据”的场景,结果完全不同

首先要把“修改数据”的场景拆开来,这是理解的关键——是在同一个SqlSession里执行增删改操作,还是外部(其他SqlSession、其他应用)修改了数据库数据?这两种情况,MyBatis的处理方式不一样,安全性也不同。

场景1:同一个SqlSession内执行增删改操作(修改对应数据)

这种情况完全不用担心查到假数据!因为MyBatis有个核心机制:在同一个SqlSession中,只要执行了insert、update、delete操作(不管是修改哪条数据,哪怕和你要查的无关),MyBatis会立刻清空当前SqlSession的一级缓存。这样后续的查询就会重新去数据库取最新数据,不会用到缓存里的旧数据。

我给你写个代码示例,一看就明白:

@TestpublicvoidtestCrudClearCache(){SqlSessionsqlSession=MyBatisUtil.getSqlSession();UserMapperuserMapper=sqlSession.getMapper(UserMapper.class);// 第一步:第一次查询,数据存入一级缓存Useruser1=userMapper.selectById(1);System.out.println("第一次查询:"+user1);// 比如输出:User{id=1, name='张三', age=20}// 第二步:同一个SqlSession里执行更新操作,修改id=1的用户数据UserupdateUser=newUser();updateUser.setId(1);updateUser.setName("张三修改后");updateUser.setAge(21);userMapper.updateById(updateUser);sqlSession.commit();// 增删改必须提交事务,这一步会触发一级缓存清空// 第三步:再次查询id=1的用户Useruser2=userMapper.selectById(1);System.out.println("第二次查询:"+user2);// 输出:User{id=1, name='张三修改后', age=21}System.out.println("两个对象是否相同:"+(user1==user2));// false,说明走了数据库sqlSession.close();}

运行这段代码,你会发现控制台打印了两次SQL执行日志,第二次查询拿到的是修改后的最新数据。原因就是:updateById操作执行并提交后,MyBatis清空了一级缓存,所以第二次查询只能去数据库查最新的。

我认为这个设计特别贴心,它优先保证了数据的一致性,哪怕牺牲一点缓存的性能,也不会让你拿到过期的假数据。

场景2:外部修改了数据库数据(同一个SqlSession期间,其他SqlSession/应用改了数据)

这种情况才是真正可能出现“查到假数据”的场景,也是MyBatis一级缓存的局限性所在。

举个例子:

  1. 我打开SqlSession A,查询了id=1的用户,数据存入A的一级缓存;
  2. 这时候,另一个用户打开SqlSession B,修改了数据库里id=1的用户数据,并且提交了事务;
  3. 我在SqlSession A里再次查询id=1的用户,这时候拿到的还是缓存里的旧数据,因为SqlSession A的一级缓存并没有被清空——MyBatis的一级缓存是本地的,它感知不到外部数据库的变化。

这种情况是不是就“不安全”了?其实也不能完全这么说,我们得结合实际的业务场景来看:

在我看来,SqlSession的生命周期通常是“一个请求对应一个SqlSession”(比如Spring整合MyBatis时,默认SqlSession是和请求绑定的,请求结束就关闭)。这种情况下,SqlSession的存活时间很短,外部修改数据恰好发生在同一个SqlSession期间的概率其实不高。但如果你的SqlSession存活时间很长(比如手动创建的SqlSession,一直不关闭),那这种数据不一致的问题就会很明显。

怎么解决“外部修改数据导致缓存不一致”的问题?

既然存在这种潜在的风险,我们在实际开发中就需要有应对的办法。我们的经验是,主要有这几种思路:

1. 缩短SqlSession的生命周期(最常用)

这是最简单也最有效的办法。比如在Spring项目中,我们会让SqlSession和Spring的事务管理器绑定,或者让SqlSession的生命周期对应一次HTTP请求——请求进来创建SqlSession,请求结束关闭SqlSession,一级缓存也就跟着销毁了。这样一来,缓存的存活时间极短,基本不会遇到外部修改数据的情况。

2. 手动清空一级缓存

如果确实需要长时间持有SqlSession,可以在关键查询前手动调用sqlSession.clearCache()方法,清空当前SqlSession的一级缓存,这样查询就会去数据库取最新数据。

// 在查询前手动清空缓存sqlSession.clearCache();Useruser=userMapper.selectById(1);

3. 对关键查询禁用一级缓存(不推荐,除非必要)

MyBatis其实没有直接禁用一级缓存的配置,但可以通过一些“小技巧”绕开,比如在查询语句后加一个随机参数(但这样会失去一级缓存的优化效果,不建议常用)。

// 不推荐:通过添加随机参数绕开一级缓存@Select("SELECT id, name, age FROM user WHERE id = #{id} AND random = #{random}")UserselectByIdWithRandom(@Param("id")Integerid,@Param("random")Doublerandom);// 调用时传入随机数userMapper.selectByIdWithRandom(1,Math.random());

4. 结合二级缓存+第三方分布式缓存(适合分布式系统)

如果是分布式项目,多个应用节点之间的数据一致性问题会更突出,这时候可以用MyBatis的二级缓存配合Redis、Ehcache等第三方缓存。这些分布式缓存可以配置缓存刷新策略,或者在数据修改时手动通知其他节点清空缓存,从而保证数据一致性。不过这种方式配置起来更复杂,需要权衡成本和收益。

总结:MyBatis缓存的“安全性”是相对的

回到最开始的问题:MyBatis的缓存是不是很不安全?

我认为答案是:在合理使用的场景下,它是安全的;但如果使用方式不当(比如长时间持有SqlSession),就可能出现数据不一致的问题

MyBatis的一级缓存设计是偏向“性能优化”的,但它优先通过“增删改清空缓存”的机制保证了同一个SqlSession内的数据一致性;而对于外部修改数据的情况,它并没有做自动处理(也没法做,因为本地缓存感知不到外部变化),这就需要我们在开发中通过规范SqlSession的使用、手动清空缓存等方式来规避风险。

说白了,任何缓存都存在“缓存一致性”的问题,不只是MyBatis,Redis、Memcached这些缓存中间件也一样。我们不能因为有这个潜在问题就不用缓存,而是要根据业务场景选择合适的缓存策略,在性能和数据一致性之间找到平衡。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/10 21:35:06

LangFlow Reactor反应器模式响应事件

LangFlow Reactor反应器模式响应事件 在构建智能对话系统、自动化内容生成流程或复杂推理代理时,开发者常常面临一个共同的挑战:如何快速验证想法,同时保证系统的可维护性和团队协作效率?传统的代码开发方式虽然灵活,但…

作者头像 李华
网站建设 2026/7/8 23:37:58

ECharts 饼图(Pie Chart)教程

ECharts 饼图(Pie Chart)教程 ECharts 的饼图(series.type ‘pie’)主要用于展示数据在总体中的占比比例,支持实心饼图、圆环图(南丁格尔图/玫瑰图)、多层嵌套等变体。数据通过 data 数组中的…

作者头像 李华
网站建设 2026/7/10 18:40:00

Open-AutoGLM日志加密部署难题:90%团队忽略的2个致命风险点

第一章:Open-AutoGLM 日志数据加密存储在 Open-AutoGLM 系统中,日志数据的安全性至关重要。为防止敏感信息泄露,系统采用端到端加密机制对日志进行安全存储。所有日志在写入磁盘前均经过加密处理,确保即使存储介质被非法获取&…

作者头像 李华
网站建设 2026/7/8 19:43:41

精密机械工厂6个研发如何共享一台SolidWorks云工作站

在精密机械制造行业,产品研发效率与协同能力直接决定市场竞争力。当6名工程师需要共享SolidWorks云工作站时,可借助智能共享云桌面,通过以下综合方案实现共享一台SolidWorks云工作站的目标,该方案涵盖硬件配置、资源管理、软件部署…

作者头像 李华
网站建设 2026/7/10 5:55:43

Open-AutoGLM监控总失效?99%人忽略的3个配置陷阱

第一章:Open-AutoGLM异常访问监控配置在部署 Open-AutoGLM 服务时,保障系统安全与稳定性至关重要。异常访问监控是及时发现潜在攻击、滥用行为或系统故障的关键机制。通过合理配置日志采集、访问频率限制和行为分析规则,可有效识别并阻断异常…

作者头像 李华
网站建设 2026/7/10 6:37:40

LangFlow静态站点生成(SSG)可行性探讨

LangFlow静态站点生成(SSG)可行性探讨 在AI应用开发日益普及的今天,越来越多团队希望快速构建、展示并共享基于大语言模型的工作流。然而,传统的LangChain项目往往依赖复杂的代码结构和持续运行的服务端环境,使得非技术…

作者头像 李华