news 2026/4/27 23:05:48

Java基础 | MyBatis的缓存机制(一级、二级)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java基础 | MyBatis的缓存机制(一级、二级)

Java基础 | MyBatis的缓存机制(一级、二级)

  • 前言
  • 一、 一级缓存(SqlSession 会话级缓存)
    • 1. 核心定义(默认生效,无需配置)
    • 2. 实战核心特性(踩坑重点)
    • 3. 实战代码示例(验证缓存生效)
  • 二、 二级缓存(Mapper 命名空间级缓存)
    • 1. 核心定义(非永久,需手动配置)
    • 2. 生效前提(缺一不可,项目必看)
    • 3. 三种实现方式(按场景选型,经验导向)
      • 方式 1:XML 配置本地二级缓存(单应用首选)
      • 方式 2:注解配置本地二级缓存(纯注解开发首选)
      • 方式 3:整合 Redis 实现分布式二级缓存(分布式场景唯一推荐)
    • 4. 三种实现方式对比与选型建议(经验总结)
    • 5. 二级缓存实战避坑指南(必看)
  • 三、 一级缓存 vs 二级缓存 核心区别(清晰对比)
  • 四、 总结

前言

MyBatis 内置的一级缓存+二级缓存机制,是项目中优化数据库查询性能的核心手段。二者在作用域、生命周期、配置方式上差异显著,结合实际业务场景选择合适的缓存策略,能大幅减少数据库 IO 压力。

一、 一级缓存(SqlSession 会话级缓存)

1. 核心定义(默认生效,无需配置)

一级缓存是SqlSession 作用域的本地缓存,MyBatis 启动即默认开启,无法手动关闭

  • 同一SqlSession内,执行完全相同的 SQL 查询(SQL 语句、参数、环境一致)时,首次查询会从数据库获取数据并写入一级缓存;后续查询直接读取缓存,无需走数据库。
  • 生命周期与SqlSession强绑定:SqlSession关闭/提交/回滚后,一级缓存会被立即清空,数据不会持久化。

2. 实战核心特性(踩坑重点)

特性详情项目经验总结
作用域单个 SqlSession 隔离不同 SqlSession 完全不共享缓存,避免多会话数据干扰
存储介质JVM 堆内存读写速度极快,但受 JVM 内存限制,不能缓存大量数据
失效场景1. 执行update/delete/insert操作(自动清空缓存,保证数据一致性)
2. SqlSession 关闭/提交/回滚
3. 手动调用sqlSession.clearCache()
4. 查询条件/参数不同
开发中不要依赖一级缓存做跨会话数据复用,否则会导致数据不一致

3. 实战代码示例(验证缓存生效)

// 1. 获取 SqlSession(自动提交事务)SqlSessionsqlSession=MyBatisUtil.getSqlSession(true);UserMapperuserMapper=sqlSession.getMapper(UserMapper.class);// 2. 首次查询:无缓存 → 查数据库 → 写入一级缓存Useruser1=userMapper.selectById(1);System.out.println("首次查询:"+user1);// 3. 二次查询:同 SqlSession + 同 SQL → 读一级缓存Useruser2=userMapper.selectById(1);System.out.println("是否同一对象:"+(user1==user2));// true(内存直接复用)// 4. 执行更新操作 → 触发一级缓存失效userMapper.updateById(newUser(1,"新名字"));// 5. 三次查询:缓存失效 → 重新查数据库Useruser3=userMapper.selectById(1);System.out.println("更新后是否同一对象:"+(user1==user3));// false// 6. 关闭 SqlSession → 一级缓存彻底销毁sqlSession.close();

二、 二级缓存(Mapper 命名空间级缓存)

1. 核心定义(非永久,需手动配置)

二级缓存是Mapper 命名空间作用域的缓存,作用于同一 Mapper 下的所有 SqlSession,是跨会话共享的缓存。

  • 缓存优先级:查询时遵循一级缓存 → 二级缓存 → 数据库的顺序,一级缓存未命中才会查二级缓存。
  • 数据同步时机:只有当SqlSession关闭/提交时,才会将一级缓存中的数据同步到二级缓存
  • 关键结论:二级缓存不是永久有效!默认按 LRU 策略淘汰冷数据,还可手动配置过期时间,达到时间后自动清空。

2. 生效前提(缺一不可,项目必看)

  1. 全局配置开启二级缓存开关(mybatis-config.xmlcacheEnabled=true,默认开启)。
  2. 对应 Mapper 显式配置开启二级缓存(XML 或注解方式)。
  3. 缓存的实体类必须实现Serializable接口(缓存序列化/反序列化需要,否则直接报错)。

3. 三种实现方式(按场景选型,经验导向)

方式 1:XML 配置本地二级缓存(单应用首选)

适用场景:单体应用、查询多修改少的表(如字典表、配置表),无需引入额外依赖,配置灵活。

步骤 1:全局配置(可省略,默认开启)

<configuration><settings><!-- 全局开关,true 开启,false 全局禁用二级缓存 --><settingname="cacheEnabled"value="true"/></settings></configuration>

步骤 2:Mapper 配置(核心,支持过期时间)

<!-- UserMapper.xml --><mappernamespace="com.example.mapper.UserMapper"><!-- 二级缓存核心配置:非永久缓存,带淘汰策略+过期时间 --><cache eviction="LRU"<!-- 淘汰策略:LRU(最近最少使用,推荐),可选 FIFO/SOFT/WEAK -->flushInterval="60000"<!-- 过期时间:60秒自动清空缓存,避免数据长期过期 -->size="1024"<!-- 缓存上限:最多存1024个对象,防止内存溢出 -->readOnly="false"<!-- 读写模式:false(默认,支持更新缓存),true(只读,性能更高) -->/><!-- 查询语句:默认启用二级缓存,可通过 useCache=false 禁用 --><selectid="selectById"resultType="com.example.entity.User">select * from user where id = #{id}</select><!-- 增删改语句:默认 flushCache=true,执行后清空当前 Mapper 二级缓存(必须!) --><updateid="updateById"parameterType="com.example.entity.User">update user set name = #{name} where id = #{id}</update></mapper>

步骤 3:实体类序列化(必做)

publicclassUserimplementsSerializable{privatestaticfinallongserialVersionUID=1L;// 显式指定序列化版本号,避免反序列化异常privateIntegerid;privateStringname;// getter/setter/toString}

实战验证:跨 SqlSession 共享缓存

// SqlSession1:查询 → 关闭 → 同步数据到二级缓存SqlSessionsqlSession1=MyBatisUtil.getSqlSession(true);Useruser1=sqlSession1.getMapper(UserMapper.class).selectById(1);sqlSession1.close();// 关键:关闭会话才会同步到二级缓存// SqlSession2:同 Mapper 查询 → 直接读二级缓存SqlSessionsqlSession2=MyBatisUtil.getSqlSession(true);Useruser2=sqlSession2.getMapper(UserMapper.class).selectById(1);System.out.println("跨会话是否同一对象:"+(user1==user2));// false(序列化后新对象)sqlSession2.close();// 等待 60 秒(超过 flushInterval)→ 二级缓存自动过期Thread.sleep(60000);// SqlSession3:缓存过期 → 重新查数据库SqlSessionsqlSession3=MyBatisUtil.getSqlSession(true);Useruser3=sqlSession3.getMapper(UserMapper.class).selectById(1);System.out.println("过期后查询:"+user3);sqlSession3.close();

方式 2:注解配置本地二级缓存(纯注解开发首选)

适用场景:Spring Boot 纯注解项目,无需 XML 文件,配置简洁。

importorg.apache.ibatis.annotations.CacheNamespace;importorg.apache.ibatis.annotations.Select;importorg.apache.ibatis.cache.decorators.LruCache;importorg.apache.ibatis.cache.impl.PerpetualCache;// 注解替代 XML 的 <cache> 标签,参数完全对应@CacheNamespace(implementation=PerpetualCache.class,// 缓存基础实现类eviction=LruCache.class,// LRU 淘汰策略flushInterval=60000,// 60秒过期size=1024,// 缓存上限readWrite=true// 读写模式)publicinterfaceUserMapper{@Select("select * from user where id = #{id}")UserselectById(Integerid);}

经验总结:注解配置和 XML 配置功能完全一致,纯注解项目选这个,混合项目推荐 XML(可读性更高)。

方式 3:整合 Redis 实现分布式二级缓存(分布式场景唯一推荐)

核心痛点:本地二级缓存(JVM 内存)在微服务/集群部署时,会出现缓存一致性问题(比如服务 A 修改数据清空缓存,服务 B 缓存还是旧数据)。
解决方案:用 Redis 替换本地缓存,实现跨服务缓存共享,这是工业级分布式项目的标准方案。

步骤 1:引入依赖(Maven)

<!-- MyBatis Redis 缓存适配器 --><dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-redis</artifactId><version>1.0.0-beta2</version></dependency><!-- Redis 客户端 --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version></dependency>

步骤 2:配置 Redis 连接(redis.properties)

redis.host=127.0.0.1 redis.port=6379 redis.password= # 无密码留空 redis.database=0 redis.timeout=2000

步骤 3:Mapper 配置 Redis 缓存

<!-- UserMapper.xml --><mappernamespace="com.example.mapper.UserMapper"><!-- 替换默认缓存为 Redis 缓存 --><cachetype="org.mybatis.caches.redis.RedisCache"><!-- Redis 缓存过期时间,单位秒 --><propertyname="expiration"value="60"/></cache><selectid="selectById"resultType="com.example.entity.User">select * from user where id = #{id}</select></mapper>

经验总结:分布式场景下,本地二级缓存等于“坑”,必须用 Redis 实现缓存共享;Redis 缓存支持持久化,应用重启后缓存不会丢失。

4. 三种实现方式对比与选型建议(经验总结)

实现方式适用场景核心优势核心劣势选型优先级
XML 配置本地缓存单体应用、查询多修改少的表配置灵活、无额外依赖、支持过期时间不支持分布式,多服务缓存不一致单体应用 →首选
注解配置本地缓存纯注解开发的单体项目无需 XML,开发效率高复杂配置可读性差、不支持分布式纯注解单体 → 次选
Redis 分布式缓存微服务/集群部署、跨服务共享缓存缓存一致性强、支持持久化、无单点问题需要部署 Redis、引入额外依赖分布式场景 →唯一推荐

5. 二级缓存实战避坑指南(必看)

  1. 不适合开启二级缓存的表:频繁更新的表(订单表、库存表),会频繁清空缓存,反而降低性能;数据一致性要求极高的表(金融交易表),缓存存在短暂延迟。
  2. 适合开启的表:查询多、修改少的静态表(字典表、地区表、配置表)。
  3. 参数配置经验值flushInterval建议设为 30-60 秒,size建议根据业务设为 512-2048,避免内存溢出。
  4. 不要忽略序列化:实体类未实现Serializable会直接抛出NotSerializableException,这是新手最常踩的坑。

三、 一级缓存 vs 二级缓存 核心区别(清晰对比)

对比维度一级缓存二级缓存
作用域SqlSession 会话级Mapper 命名空间级
共享性不同 SqlSession 不共享同 Mapper 的所有 SqlSession 共享
开启方式默认开启,无法关闭需全局+Mapper 手动配置
存储介质仅 JVM 堆内存默认 JVM 内存,可扩展为 Redis 等
数据同步时机查询后立即写入SqlSession 关闭/提交时从一级缓存同步
生命周期随 SqlSession 销毁而销毁非永久,可配置过期时间,随应用启停
对象一致性同会话查询返回同一个对象(== true)跨会话返回序列化新对象(== false)
适用场景单会话内重复查询跨会话重复查询、分布式共享数据

四、 总结

MyBatis 缓存的核心是“一级缓存解决单会话重复查询,二级缓存解决跨会话重复查询”

  • 单体应用优先用XML 配置的本地二级缓存,简单高效;
  • 分布式应用必须用Redis 分布式二级缓存,解决一致性问题;
  • 无论哪种缓存,都要结合业务场景,避免盲目开启,否则会适得其反。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 14:32:20

基于51单片机的摩尔斯电码收发控制系统设计

第一章&#xff1a;设计背景与意义 摩尔斯电码作为一种古老而可靠的编码方式&#xff0c;在通信历史上具有重要地位&#xff0c;至今仍在应急通信、无线电爱好者领域发挥作用。传统摩尔斯电码通信依赖人工操作电键&#xff0c;编码和解码效率低&#xff0c;且易受人为因素影响导…

作者头像 李华
网站建设 2026/4/17 17:45:22

建筑抗震模拟:TensorFlow有限元分析加速

建筑抗震模拟&#xff1a;TensorFlow有限元分析加速 在超高层建筑林立、城市密度不断攀升的今天&#xff0c;一场大地震可能带来的不仅是结构损毁&#xff0c;更是对公共安全的巨大威胁。传统上&#xff0c;工程师依赖复杂的有限元软件如 ABAQUS 或 OpenSees 进行抗震仿真——一…

作者头像 李华
网站建设 2026/4/22 19:39:39

Open-AutoGLM 2.0缺陷全曝光(专家级避坑指南)

第一章&#xff1a;Open-AutoGLM 2.0缺陷全曝光&#xff08;专家级避坑指南&#xff09;模型推理延迟异常 在高并发场景下&#xff0c;Open-AutoGLM 2.0 的推理服务表现出显著延迟。经测试&#xff0c;在每秒超过50次请求时&#xff0c;平均响应时间从200ms飙升至1.8s&#xff…

作者头像 李华
网站建设 2026/4/25 8:07:47

2025最新!自考必看8款AI论文软件测评与推荐

2025最新&#xff01;自考必看8款AI论文软件测评与推荐 2025年自考AI论文工具测评&#xff1a;精准选择提升写作效率 随着人工智能技术的不断进步&#xff0c;AI论文工具在自考学习中的应用日益广泛。然而&#xff0c;面对市场上种类繁多的软件&#xff0c;如何挑选真正适合自己…

作者头像 李华
网站建设 2026/4/22 21:51:07

【Open-AutoGLM插件安装全指南】:手把手教你5步完成浏览器部署

第一章&#xff1a;Open-AutoGLM插件安装全指南 Open-AutoGLM 是一款基于 GLM 大模型生态的自动化工具插件&#xff0c;广泛应用于智能问答、代码生成与任务编排场景。正确安装并配置该插件是实现高效开发的前提。以下将详细介绍其安装流程与环境准备。 环境依赖准备 在安装 O…

作者头像 李华
网站建设 2026/4/21 19:43:55

渔业资源评估:TensorFlow鱼类数量统计模型

渔业资源评估&#xff1a;TensorFlow鱼类数量统计模型 在海洋与淡水生态系统日益脆弱的今天&#xff0c;过度捕捞、栖息地破坏和气候变化正威胁着全球渔业资源的可持续性。传统的鱼类种群调查依赖潜水员目视计数或声呐设备探测&#xff0c;不仅成本高昂、人力密集&#xff0c;还…

作者头像 李华