MyBatis缓存机制

dalang · · 123 次点击 · · 开始浏览    
--- ### **一、一级缓存的核心特性** 1. **作用范围** 一级缓存是 **SqlSession 级别**的缓存,仅在同一个数据库会话(SqlSession)内部有效。 • **示例**: ```java SqlSession session1 = sqlSessionFactory.openSession(); User user1 = session1.selectOne("getUserById", 1); // 首次查询,写入缓存 User user2 = session1.selectOne("getUserById", 1); // 命中缓存 ``` 此时 `user1` 和 `user2` 是同一个对象,说明缓存生效。 2. **生命周期** 缓存数据在以下情况会被清空: • SqlSession 关闭(`session.close()`) • 执行增删改操作(INSERT/UPDATE/DELETE) • 显式调用 `session.clearCache()`。 --- ### **二、脏数据问题的根源** 1. **多 SqlSession 场景** 当存在多个 SqlSession 时,**每个会话的缓存相互独立**。例如: ```java SqlSession session1 = sqlSessionFactory.openSession(); SqlSession session2 = sqlSessionFactory.openSession(); User user1 = session1.selectOne("getUserById", 1); // 查询并缓存 session2.update("updateUser", new User(1, "新名字")); // 另一个会话更新数据 User user2 = session1.selectOne("getUserById", 1); // 仍读取旧缓存 ``` 此时 `user2` 获取的是未更新的旧数据,导致脏读。 2. **分布式环境风险** 在分布式系统中,不同节点的 SqlSession 完全隔离。若节点 A 更新数据,节点 B 的缓存无法感知变化,仍返回旧数据。 --- ### **三、解决方案:设定缓存级别为 STATEMENT** 1. **配置方法** 在 MyBatis 全局配置中修改 `localCacheScope` 参数: ```xml <settings> <setting name="localCacheScope" value="STATEMENT"/> </settings> ``` • `SESSION`(默认):启用一级缓存 • `STATEMENT`:关闭一级缓存,每次查询后清空缓存。 2. **底层原理** 当设置为 `STATEMENT` 时,MyBatis 会在 **每次执行 SQL 后立即清空缓存**,确保每次查询都直接访问数据库,避免缓存与数据库数据不一致。 --- ### **四、实际场景中的权衡** 1. **何时保留一级缓存** • 单线程、短生命周期的简单查询(如管理系统中的静态数据展示) • 对性能要求高且数据更新频率极低的场景。 2. **何时禁用一级缓存** • 高并发或分布式环境 • 需要强数据一致性的业务(如金融交易系统) • 多表关联查询且关联表可能被其他会话修改的场景。 --- ### **五、补充:与 Spring 整合时的特殊情况** 当 MyBatis 与 Spring 整合时: • **默认未开启事务**:每次数据库操作会创建新的 SqlSession,一级缓存实际无效。 • **开启事务**:同一个事务内复用 SqlSession,此时一级缓存生效,需特别注意脏数据风险。 --- ### **总结** • **问题本质**:一级缓存的隔离性导致多会话或分布式环境下数据更新不可见。 • **核心建议**:在需要强一致性的场景中,通过 `localCacheScope=STATEMENT` 禁用一级缓存,或改用二级缓存(需注意二级缓存的多表关联问题)。 • **扩展方案**:分布式环境下可集成 Redis 等集中式缓存,但需处理缓存穿透、雪崩等问题。
123 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传