MyBatis原生二级缓存为本地内存缓存,多节点部署下易导致脏读;推荐替换为Redis等分布式缓存,或通过事件广播、关闭二级缓存等方式保障一致性。

MyBatis 原生二级缓存是本地内存缓存,不具备集群间同步能力。在多节点部署(如 Nginx 负载均衡 + 多个 SpringBoot 实例)场景下,各节点维护独立的二级缓存,一旦某个节点更新数据但未通知其他节点清空对应缓存,就会产生脏读——其他节点仍返回过期数据。
用分布式缓存替代原生二级缓存
最直接有效的方案是将 MyBatis 二级缓存的存储后端从 JVM 内存切换为 Redis、Etcd 或 Hazelcast 等支持跨节点共享与失效通知的分布式缓存系统。
- MyBatis-Plus 用户可配置
cache.type=redis,并引入spring-boot-starter-data-redis依赖,自动集成 RedisCache - 原生 MyBatis 可自定义
Cache实现类(如继承TransactionalCache),底层操作 Redis 的 key-value,并确保增删改操作触发clear()时向 Redis 发送删除指令 - 关键点:所有节点共用同一套 Redis 实例或集群,且缓存 key 设计需包含 namespace + SQL 参数哈希,保证唯一性和可预测性
严格控制缓存粒度与更新边界
避免跨 Mapper 更新引发的缓存不同步(例如 DeptDao 查询关联 dept+deptNum,而 DeptNumDao 单独更新人数),这类场景原生二级缓存天然无法感知。
- 对涉及多表关联的查询,禁用二级缓存(
<select ... useCache="false">),或仅对单表、低频变更的数据启用 - 更新操作统一收口到特定 Mapper,并在对应
<update>标签中保留默认flushCache="true";若使用注解,不显式设置flushCache = false - 对高一致性要求的业务(如订单状态、账户余额),查询语句主动设
flushCache="true",每次查前先清本 namespace 缓存,强制回源数据库
借助事件机制实现跨节点缓存失效
当无法替换缓存组件时,可通过应用层广播机制协调各节点缓存清理。
立即学习“Java免费学习笔记(深入)”;
- 利用 Spring 的
ApplicationEvent+@EventListener,在更新成功后发布“DeptNumUpdatedEvent” - 各节点监听该事件,调用本地
Cache.clear()清除相关 namespace 缓存(如DeptDao.class.getName()) - 配合消息中间件(如 RocketMQ、Kafka)实现可靠广播,避免事件丢失;注意设置消费幂等,防止重复清缓存
关闭二级缓存,专注一级缓存与数据库优化
并非所有场景都需要二级缓存。若业务更新频繁、一致性优先级极高,或集群规模小且数据库压力可控,可选择关闭二级缓存,回归更可控的一致性模型。
- 在
application.yml中设mybatis.configuration.cache-enabled: false - 保留一级缓存(默认开启),它天然隔离在单个 SqlSession 内,无跨节点污染风险
- 配合数据库连接池调优、SQL 索引优化、读写分离等手段,同样可支撑中高并发读场景


















