MyBatis三级缓存机制深度解析:从原理到实战避坑

1. 项目概述:MyBatis三级缓存机制深度剖析

如果你用过MyBatis,肯定对“缓存”这个词不陌生。面试的时候,面试官也总爱问:“聊聊MyBatis的一级缓存和二级缓存?” 但很多人可能不知道,或者只是模糊地听说过,MyBatis其实存在一个“三级缓存”的概念。这并不是官方文档里明确划分的一个独立层级,而是我们开发者根据其缓存的作用域和生命周期,结合Spring等框架的集成,在实践中总结出来的一套完整的缓存视图。今天,我们就来彻底拆解这个“三级缓存”体系,搞明白每一级缓存是什么、怎么工作、以及最关键的——它们在实际项目中是怎么相互配合,又是怎么给我们“挖坑”的。

简单来说,MyBatis的三级缓存可以理解为:

  • 一级缓存(SqlSession级别) :默认开启,作用域最小,生命周期与一次数据库会话绑定。
  • 二级缓存(Mapper/Namespace级别) :需要手动配置开启,作用域跨SqlSession,可以被多个会话共享。
  • 三级缓存(应用/框架集成级别) :这通常指的是集成如Spring Cache、Redis等外部缓存框架后形成的应用级缓存,其作用域最大,可以跨应用实例共享。

理解这套机制,不仅能帮你解决“为什么我查的数据是旧的?”、“开了事务怎么查不到最新数据?”这类诡异问题,更是优化应用性能、设计高可用数据访问层的必备知识。无论你是正在被MyBatis缓存问题困扰的开发者,还是准备面试想深入原理的求职者,这篇文章都将带你从使用到源码,从配置到避坑,完整地走一遍。

2. 三级缓存整体架构与核心设计思想

在深入每一级缓存之前,我们得先站在高处,看看MyBatis设计这套缓存机制的初衷和整体蓝图。MyBatis作为一个半自动化的ORM框架,其核心价值之一就是在对象和关系数据库之间提供灵活、高效的映射。缓存,就是为了减少对数据库的直接访问,这个目标服务的。

2.1 为什么需要多级缓存?

想象一下图书馆借书。一级缓存就像你手边正在看的这本书,取用最快,但离开座位(关闭SqlSession)就得还回去。二级缓存像是这个阅览室的书架,同一个房间(同一个Mapper)的人都能看,但别的阅览室(别的Mapper)的人拿不到。三级缓存则像是图书馆的中央书库,所有读者(甚至其他分馆的应用)在权限内都可以借阅。

MyBatis采用这种分层设计,核心思想是 在数据一致性、性能与资源消耗之间取得平衡

  1. 性能与速度 :一级缓存速度最快,因为数据就在内存中的SqlSession对象里。二级缓存次之,需要序列化/反序列化和跨会话查找。三级缓存(如Redis)可能涉及网络IO,速度最慢,但共享能力最强。
  2. 数据一致性 :缓存层级越高,数据共享范围越广,保持一致性就越复杂。一级缓存只影响自己,很容易通过关闭会话来清空。二级缓存需要处理多个会话的并发更新。三级缓存则要面对分布式环境下的数据同步难题。
  3. 作用域与生命周期 :这是分级的关键。从一次请求(SqlSession)、到一个业务模块(Mapper)、再到整个应用乃至集群,缓存的生命周期和作用域逐级扩大,应对不同的场景需求。

2.2 各级缓存的核心职责与交互关系

三级缓存并非完全独立,它们在执行一次查询时,遵循着一个清晰的查询链,这通常被称为 缓存查询顺序

当执行一条查询语句时(例如 select * from user where id = #{id} ),MyBatis的 Executor 执行器会按以下顺序查找数据:

  1. 首先查询一级缓存 :检查当前 SqlSession 中是否存在该查询的缓存结果。如果命中,直接返回,不会执行后续步骤。这是最快的一条路径。
  2. 然后查询二级缓存 :如果一级缓存未命中,且当前 Mapper 配置启用了二级缓存,则查询二级缓存。二级缓存底层是一个 PerpetualCache 对象,但被各种装饰器(如序列化、LRU淘汰、同步锁等)包装,存储的是序列化后的数据。如果命中,将数据反序列化后返回,同时 放入当前SqlSession的一级缓存中 (注意这点,很重要)。
  3. 最后查询数据库 :如果一、二级缓存均未命中,则执行JDBC操作,访问数据库获取数据。拿到数据后:
    • 将结果放入 当前SqlSession的一级缓存
    • 如果启用了二级缓存,同时会将结果放入 二级缓存 (同样需要序列化)。

至于 三级缓存 ,它通常不在MyBatis这个默认查询链里。它更像是一个“旁路缓存”,由开发者通过Spring的 @Cacheable 注解或手动操作Redis客户端来实现。它的优先级和集成方式由应用架构决定,可能在一、二级缓存之前拦截,也可能作为二级缓存的分布式存储后端。

注意 :这个顺序解释了为什么有时你开启了二级缓存,感觉效果却不明显。因为如果你的操作都在同一个SqlSession内(比如一个事务方法中多次查询同一数据),请求会被一级缓存拦截,根本走不到二级缓存那一步。

3. 一级缓存:SqlSession级别的“私人工作区”

一级缓存是MyBatis中最基础、最直接的缓存,理解它是理解所有缓存问题的起点。

3.1 工作原理与生命周期

一级缓存本质上是一个HashMap,它的键是 CacheKey 。这个 CacheKey 由多个要素共同决定,确保查询的唯一性:

  • Mapper的Id(即命名空间+方法名)
  • 查询的偏移量(分页参数)
  • 查询的SQL语句本身
  • 传递给SQL的实际参数值
  • 环境Id(比如你配置的多数据源)

只要这些要素完全相同,MyBatis就认为这是同一次查询,会尝试从一级缓存中直接返回结果。

它的生命周期与 SqlSession 绑定:

  • 创建 :当调用 SqlSessionFactory.openSession() 方法时,一个新的 SqlSession 被创建,同时一个全新的一级缓存(一个 PerpetualCache 对象)也随之创建。
  • 存活 :在该 SqlSession 存活期间,所有查询的结果(除非配置了 flushCache=true )都会被存入这个HashMap。
  • 销毁 :调用 SqlSession.close() 方法,或者这个 SqlSession 对象被垃圾回收时,一级缓存随之销毁。这也是为什么我们常说“一级缓存默认是开启的”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值