---
title: "MyBatis二级缓存机制深度解析：源码与实战 | 万维易源"
canonical_url: "https://www.showapi.com/news/article/6a767c114ddd79ab6700e76b"
last_updated: "2026-08-08T01:10:02.183Z"
meta:
  description: " 本文深入剖析MyBatis二级缓存机制的源码实现，聚焦其跨SqlSession共享特性与作用域边界——即限定在Mapper的namespace内。通过追踪Cache接口、CachingExecutor及PerpetualCache等核心类的协作逻辑，揭示二级缓存如何在Mapper接口或XML文件粒度上实现数据复用，显著提升查询性能。文章结合关键流程节点与典型配置场景，厘清启用条件、失效策略及线程安全设计，为开发者提供可落地的源码级理解路径。  "
  keywords: "MyBatis 二级缓存 源码解析 SqlSession Mapper AI资讯 AIGC资讯  "
  "og:description": " 本文深入剖析MyBatis二级缓存机制的源码实现，聚焦其跨SqlSession共享特性与作用域边界——即限定在Mapper的namespace内。通过追踪Cache接口、CachingExecutor及PerpetualCache等核心类的协作逻辑，揭示二级缓存如何在Mapper接口或XML文件粒度上实现数据复用，显著提升查询性能。文章结合关键流程节点与典型配置场景，厘清启用条件、失效策略及线程安全设计，为开发者提供可落地的源码级理解路径。  "
  "og:title": MyBatis二级缓存机制深度解析：源码与实战
---

*

*

*

*

# MyBatis二级缓存机制深度解析：源码与实战

文章提交： [bt69a](https://www.showapi.com/)

2026-08-08

MyBatis二级缓存源码解析SqlSession

本文由 AI 阅读网络公开技术资讯生成，力求客观但可能存在信息偏差，具体技术细节及数据请以权威来源为准

\> ### 摘要 > 本文深入剖析MyBatis二级缓存机制的源码实现，聚焦其跨SqlSession共享特性与作用域边界——即限定在Mapper的namespace内。通过追踪Cache接口、CachingExecutor及PerpetualCache等核心类的协作逻辑，揭示二级缓存如何在Mapper接口或XML文件粒度上实现数据复用，显著提升查询性能。文章结合关键流程节点与典型配置场景，厘清启用条件、失效策略及线程安全设计，为开发者提供可落地的源码级理解路径。 > ### 关键词 > MyBatis,二级缓存,源码解析,SqlSession,Mapper ## 一、二级缓存的基本概念与作用 ### 1.1 二级缓存的定义：跨SqlSession共享的缓存机制 二级缓存是MyBatis框架中的一项功能，它允许跨SqlSession共享缓存——这一特性，宛如在多个独立会话之间悄然架起一座数据桥梁。不同于每个SqlSession独享的一级缓存，二级缓存突破了单一会话的边界，让不同SqlSession在执行同一Mapper下的查询时，能从同一个缓存容器中读取已加载的数据。这种共享并非无序泛化，而是严格受控于MyBatis的运行契约：它不跨越Mapper的界限，也不穿透命名空间的壁垒。开发者每一次调用\`sqlSession.getMapper(XxxMapper.class)\`所触发的查询，只要属于同一namespace，就天然被纳入这个协同复用的缓存生态。它不喧哗，却在高并发读场景下默默分担数据库压力；它不显眼，却正是MyBatis“可插拔缓存”设计哲学最沉稳的落地表达。 ### 1.2 二级缓存的作用域：Mapper namespace内的共享 二级缓存的作用域限定在Mapper的namespace内，即所有属于同一Mapper接口或XML文件中的SQL语句可以共享同一个二级缓存。这是一条清晰而不可逾越的边界线——它既不是以整个应用为单位，也不是按表或按SQL ID粗粒度划分，而是精准锚定在namespace这一逻辑单元上。一个\`UserMapper.xml\`文件所声明的所有\`\<select>\`、\`\<insert>\`等标签，无论分散在多少个方法中，只要其\`namespace="com.example.mapper.UserMapper"\`，便自动归入同一缓存实例；而\`OrderMapper\`哪怕与之结构相似、字段重叠，也绝不会染指其缓存数据。这种设计，既保障了缓存隔离的安全性，又赋予了开发者按业务模块精细调控缓存策略的能力。namespace，因此不只是XML的标识符，更是缓存世界的国界碑。 ### 1.3 二级缓存与一级缓存的区别与联系 一级缓存作用域限于单个SqlSession生命周期内，而二级缓存则允许跨SqlSession共享缓存，二者共同构成MyBatis两级缓存体系。一级缓存如呼吸般自然存在——无需配置，随SqlSession创建而生、随关闭而逝；二级缓存则如契约般需主动启用：既要Mapper接口或XML中声明\`\<cache/>\`，又需全局配置\`cacheEnabled=true\`。它们并非并列替代关系，而是层层递进：查询时优先命中一级缓存，未命中则交由CachingExecutor委托二级缓存处理；若二级缓存亦未命中，才真正触达数据库。这种协作逻辑，使一级缓存成为“会话内热数据”的速写板，二级缓存则担当“模块间冷数据”的共享仓库——二者共生于Cache接口抽象之下，又在PerpetualCache、SynchronizedCache等具体实现中各司其职，静默支撑着每一次\`sqlSession.selectList()\`背后的精密调度。 ### 1.4 二级缓存的适用场景与局限性 二级缓存适用于读多写少、数据变更频率低且对实时性要求不苛刻的业务场景，例如系统字典、地区编码、用户角色权限等相对静态的基础数据查询。它能在Mapper的namespace内显著提升查询性能，尤其当多个SqlSession频繁访问同一组结果集时，缓存命中率的跃升将直接降低数据库负载。然而，其局限性同样鲜明：由于作用域限定在Mapper的namespace内，无法跨namespace复用缓存，导致关联查询（如UserMapper联查RoleMapper）仍需多次穿透；同时，缓存失效依赖于同namespace下任意增删改操作的自动清空机制，缺乏细粒度刷新能力；更需警惕的是，若未正确配置序列化或启用不安全的缓存实现，可能引发线程安全问题或反序列化异常。因此，二级缓存不是银弹，而是需要开发者以源码级理解为尺，在权衡一致性、性能与复杂度之后，审慎落笔的一道架构注脚。 ## 二、二级缓存的配置与启用 ### 2.1 在Mapper XML中配置二级缓存 在MyBatis的生态里，XML曾是开发者与框架对话最沉静也最郑重的方式。当 \`\<cache/>\` 标签悄然出现在 \`UserMapper.xml\` 的根节点之下，它不单是一行标记，而是一份契约的签署——宣告该Mapper的namespace正式启用二级缓存。这一配置如一道闸门，将原本各自为政的SqlSession纳入同一缓存流域：所有属于该namespace的查询语句自此共享同一个PerpetualCache实例，所有增删改操作也将触发其自动清空。它不声张，却在\`\<mapper namespace="com.example.mapper.UserMapper">\`的边界内，织就一张细密的数据复用之网。配置可进一步扩展属性——\`eviction\`指定淘汰策略，\`flushInterval\`设定刷新周期，\`size\`约束容量上限，\`readOnly\`决定是否返回缓存对象副本——每一项都直指缓存行为的底层肌理。没有注解的灵动，却有XML特有的确定性与可追溯性，仿佛在源码尚未展开之前，逻辑已在线性文本中悄然落定。 ### 2.2 通过注解方式启用二级缓存 当Java代码成为Mapper定义的主战场，\`@CacheNamespace\`便成了二级缓存的无声宣言。它被郑重标注于\`XxxMapper\`接口之上，如同在类的元数据中刻下一道缓存印记——从此，该接口所声明的所有方法，皆被纳入同一namespace的缓存管辖范围。这种声明式启用，与XML路径形成双轨并行：二者殊途同归，最终都导向\`Cache\`接口的统一抽象与\`CachingExecutor\`的统一调度。值得注意的是，注解方式并未削弱约束力——它同样严格遵循“作用域限定在Mapper的namespace内”这一铁律；任何跨接口的缓存共享企图，都会在命名空间解析阶段被无声拦截。它轻盈，却不轻率；简洁，却未简化契约。对习惯纯Java风格开发的团队而言，这枚注解不是语法糖，而是将缓存治理权，稳稳交还给接口设计者手中的那一枚印章。 ### 2.3 缓存策略的配置：LRU、FIFO、SOFT、WEAK 缓存的生命力，不仅在于存储，更在于如何优雅地遗忘。MyBatis将淘汰策略具象为四个可配置的关键词：LRU（最近最少使用）、FIFO（先进先出）、SOFT（软引用）、WEAK（弱引用）。它们并非抽象概念，而是直接映射至\`Cache\`实现类的行为内核——LRU驱逐沉睡最久的条目，FIFO按写入时序线性清理，SOFT与WEAK则交由JVM垃圾回收机制协同裁决。这些策略被声明于\`\<cache>\`标签的\`eviction\`属性中，或通过\`@CacheNamespace\`的\`eviction\`参数注入，成为缓存容器的“呼吸节律”。选择何种策略，实则是权衡内存稳定性与命中率的艺术：LRU贴近业务访问模式，FIFO保障时间公平性，SOFT/WEAK则在堆内存紧张时主动让渡空间。它们共同服务于一个朴素目标——让二级缓存既不贪婪囤积，也不草率遗弃，在\`Mapper\`的namespace疆域内，维持一种动态的、有尊严的数据驻留秩序。 ### 2.4 缓存大小与过期时间的设置 缓存不是无限容器，它需要边界感。\`size\`属性划定容量红线——默认值为1024，意味着每个二级缓存实例最多容纳1024个键值对；一旦超限，即由所选淘汰策略介入裁决。而\`flushInterval\`则为缓存注入时间维度的节制：以毫秒为单位设定自动刷新周期，使缓存内容在指定间隔后强制失效，避免陈旧数据滞留。这两项配置，一个框定空间尺度，一个锚定时间刻度，共同构成二级缓存的“物理存在法则”。它们被严丝合缝地嵌入\`\<cache>\`标签或\`@CacheNamespace\`注解之中，不可缺省，亦不可越界——因为MyBatis的缓存契约，从不容忍无度的驻留。当开发者写下\`\<cache size="512" flushInterval="60000"/>\`，他不仅是在调参，更是在namespace的疆域内，为数据的生命周期签下一份理性而克制的协议。 ### 2.5 二级缓存的全局配置与局部配置 MyBatis的缓存治理，始终在宏观约束与微观自治之间寻求张力平衡。全局配置\`cacheEnabled=true\`是整座缓存大厦的地基——若此开关关闭，所有Mapper层级的二级缓存声明都将静默失效，无论XML中如何铺陈\`\<cache/>\`，抑或接口上如何标注\`@CacheNamespace\`。而局部配置，则是每个Mapper namespace的自主权：它可在全局开启的前提下，选择启用、定制甚至禁用自身缓存——通过\`\<cache eviction="LRU" size="256"/>\`或\`@CacheNamespace(blocking = true)\`等声明，完成精细化策略部署。这种两级配置体系，既防止了缓存滥用带来的数据一致性风险，又保留了按业务模块差异化调控的弹性空间。它不强求整齐划一，却以\`SqlSession\`为执行单元、以\`Mapper\`为作用域单元、以\`Cache\`为抽象单元，构筑起一套层次清晰、收放自如的缓存治理体系——而这，正是MyBatis作为成熟ORM框架，在“开箱即用”与“深度可控”之间，写下的最沉稳的答案。 ## 三、二级缓存的源码实现机制 ### 3.1 Cache接口及其实现类分析 Cache接口是MyBatis二级缓存体系的基石，它以极简的契约定义了缓存行为的核心语义：\`get\`、\`putObject\`、\`removeObject\`、\`clear\`与\`getSize\`——五种方法，如五根支柱，撑起整个缓存生态的抽象穹顶。它不关心数据从何而来，也不介入线程如何调度，只冷静地宣告“缓存应当能做什么”。而真正让这抽象落地的，是一系列精心编织的实现类：PerpetualCache作为默认底层容器，朴素却坚实；SynchronizedCache为其披上线程安全的外衣；LoggingCache默默记录每一次命中与未命中；SerializedCache则在序列化边界上谨慎驻守——它们并非并列平铺，而是以装饰器模式层层包裹，最终汇聚于Mapper namespace所绑定的那个唯一Cache实例。这种设计，让缓存不再是黑盒，而成为可观察、可替换、可调试的模块化组件。当开发者调用\`sqlSession.selectList()\`，背后流转的正是这一接口定义下的统一契约；而每一次\`Cache\`的实例化，都是对“作用域限定在Mapper的namespace内”这一原则最忠实地编码实现。 ### 3.2 PerpetualCache与BlockingCache的实现原理 PerpetualCache是MyBatis二级缓存最本真的容器，它不加修饰地使用\`ConcurrentHashMap\`承载键值对，像一座静默运转的仓库，只负责存储与检索，不主动淘汰、不自动刷新、不阻塞等待——纯粹、轻量、无状态。而BlockingCache则在其之上叠加了一层“等待即获取”的哲学：当多个线程同时请求同一缓存键却未命中时，它不会让所有线程一拥而上穿透数据库，而是仅放行一个线程去加载，其余线程安静阻塞、共享结果。这种机制，既缓解了缓存击穿压力，又悄然守护着数据一致性底线。二者共存于同一namespace的缓存链中，前者是数据栖居的土壤，后者是并发访问的守门人——它们不喧哗，却在\`Mapper\`的namespace疆域内，以代码为笔，写下关于效率与秩序的双重注解。 ### 3.3 二级缓存的创建与初始化过程 二级缓存的诞生，并非始于查询那一刻，而是在MyBatis启动阶段便悄然完成。当\`XMLMapperBuilder\`解析\`\<mapper>\`标签时，若发现\`\<cache/>\`声明，便会触发\`CacheBuilder\`构建流程：先依据\`eviction\`属性选择淘汰策略装饰器，再依\`readOnly\`决定是否套上SerializedCache，最终将PerpetualCache层层包裹，生成一个符合配置语义的完整Cache实例。该实例被注册进\`Configuration\`的\`cacheMap\`中，以Mapper的namespace为键，从此成为该namespace不可分割的缓存身份标识。这一过程严格遵循“作用域限定在Mapper的namespace内”的铁律——每个namespace独享一个Cache实例，彼此隔离，互不干扰。它不依赖运行时动态生成，而是在配置加载期就完成静态绑定，确保从第一个\`SqlSession\`打开起，缓存逻辑便已整装待发。 ### 3.4 缓存数据的存储与获取机制 缓存数据的存取，由CachingExecutor统一分发调度。当执行查询时，CachingExecutor首先尝试从当前Mapper namespace绑定的Cache中\`get\`——键由MappedStatement的ID与BoundSql的SQL参数共同哈希生成，确保相同SQL与参数组合始终映射至同一缓存位置；若命中，则直接返回反序列化后的结果；若未命中，则委托BaseExecutor执行真实数据库查询，并在结果返回后调用\`putObject\`写入缓存。整个过程如精密钟表般严丝合缝：\`SqlSession\`是执行单元，\`Mapper\`是作用域单元，\`Cache\`是抽象单元——三者环环相扣，在namespace的边界内完成一次无声的数据复用。它不越界，不妥协，只在属于自己的逻辑疆域里，忠实履行“跨SqlSession共享缓存”的原始承诺。 ### 3.5 缓存更新与失效策略的实现 二级缓存的失效，并非被动等待时间流逝，而是由写操作主动触发的刚性契约。只要同一Mapper namespace内发生任意\`\<insert>\`、\`\<update>\`或\`\<delete>\`语句的执行，CachingExecutor便会立即调用对应Cache的\`clear()\`方法，清空整个namespace下的全部缓存条目。这种“全量失效”策略简单、确定、零歧义，它不试图猜测哪些数据受影响，而是以最保守的方式保障读写一致性。与此同时，\`flushInterval\`配置赋予缓存以时间维度的自我更新能力——到达设定毫秒数后，自动执行一次\`clear()\`，使缓存内容强制过期。二者并行不悖：前者响应业务变更，后者防范数据陈旧。它们共同锚定在同一个前提之上——缓存的生命，必须服从于Mapper namespace内SQL语句的整体语义一致性。 ## 四、二级缓存的生命周期管理 ### 4.1 缓存的加载与初始化流程 缓存从不等待被需要，它在MyBatis的生命之初便已悄然落位。当\`XMLMapperBuilder\`逐行解析\`\<mapper>\`标签，目光触及那一行朴素的\`\<cache/>\`时，初始化的齿轮便开始转动——这不是运行时的权宜之计，而是一场在配置加载期完成的庄严赋值。\`CacheBuilder\`依令而动：先以\`eviction\`属性为引，将LRU、FIFO或SOFT等策略织入装饰链；再据\`readOnly\`开关决定是否包裹\`SerializedCache\`，为反序列化筑起安全堤坝；最终，一个被层层封装的\`PerpetualCache\`实例，在\`Configuration\`的\`cacheMap\`中以Mapper的namespace为键稳稳落座。这一刻，缓存不再是抽象概念，而是具象为内存中一段有身份、有边界、有契约的实体——它不因SqlSession的启闭而生灭，只忠于namespace的疆域法则。每一个\`sqlSession.getMapper(XxxMapper.class)\`背后，都早已站着这个静默守候的缓存化身，它不声张，却在第一个\`selectList()\`调用前，就完成了全部准备。 ### 4.2 缓存的清除与刷新机制 清除，是二级缓存最决绝的自我约束；刷新，则是它最克制的自我更新。二者皆非被动响应，而是由明确指令驱动的刚性动作：只要同一Mapper namespace内任一\`\<insert>\`、\`\<update>\`或\`\<delete>\`语句执行完毕，\`CachingExecutor\`便即刻调用对应Cache的\`clear()\`方法，整片namespace下的缓存条目如潮水退去，不留余痕——这是对数据一致性的无条件臣服。与此同时，\`flushInterval\`以毫秒为刻度，在时间维度上划下另一道清零线：一旦到达设定周期，缓存亦自动\`clear()\`，哪怕其间未发生任何写操作。两种机制并行不悖，却共享同一信仰——缓存的存在，永远 subordinate 于Mapper namespace内SQL语义的整体完整性。它不犹豫，不妥协，只在“作用域限定在Mapper的namespace内”这一铁律之下，以清除为盾、以刷新为尺，守护每一次查询背后那不容玷污的真实。 ### 4.3 事务对二级缓存的影响 事务是数据库的契约，而二级缓存是MyBatis的承诺——二者交汇之处，没有模糊地带。MyBatis并未将缓存操作纳入JDBC事务的ACID范畴，而是选择了一种更清醒的隔离：缓存的写入（\`putObject\`）发生在事务提交之后，读取（\`get\`）则始终面向已提交数据。这意味着，在事务尚未\`commit\`前，即使\`CachingExecutor\`已完成数据库写入，缓存也不会提前更新；而若事务最终\`rollback\`，缓存更不会因中间态的误写而污染。这种设计，使二级缓存天然规避了脏读风险，却也意味着它永远滞后于事务边界——它不参与事务的起承转合，只安静等待commit落定后的那一声确认。于是，在\`SqlSession\`的生命周期里，缓存与事务各自恪守职责：一个负责跨会话的数据复用，一个负责单次操作的原子保障，二者在namespace的疆域内平行运行，互不越界，亦互不辜负。 ### 4.4 缓存与数据库一致性的处理 一致性，不是缓存的终点，而是它存在的前提。MyBatis以最朴素的方式捍卫这一底线：不预测、不推断、不缓存例外——只要同一Mapper namespace内发生增删改，便全量清空缓存。这种“宁可错杀，不可放过”的策略，舍弃了细粒度失效的复杂性，换来了逻辑上的绝对确定性。它不试图识别哪几条记录被修改，也不依赖版本号或时间戳做增量同步；它只认一个事实：\`\<insert>\`、\`\<update>\`、\`\<delete>\`的执行，即意味着该namespace下所有查询结果的潜在失效。于是，\`clear()\`成为唯一且不可绕行的路径，它不优雅，却足够可靠；它不智能，却杜绝歧义。当开发者在\`UserMapper.xml\`中写下\`\<update>\`，他不仅改变了数据库，也亲手按下了该namespace缓存的重置键——这并非缺陷，而是MyBatis对“作用域限定在Mapper的namespace内”这一原则最忠实的践行：在可控的边界内，用最简的逻辑，守住最硬的一致性底线。 ### 4.5 缓存溢出与内存管理策略 缓存从不贪婪，它深知自己的容器边界。\`size\`属性是MyBatis为每个二级缓存实例划定的物理红线——默认1024，不可逾越。当键值对数量逼近此限，淘汰策略便依\`eviction\`配置即时介入：LRU驱逐沉睡最久者，FIFO清理最早入驻者，SOFT与WEAK则交由JVM在GC时协同裁决。这些策略并非抽象术语，而是直接嵌入\`Cache\`实现类的行为内核，构成缓存容器的呼吸节律。它们共同服务于一个清醒的认知：缓存不是永驻的仓库，而是动态流转的数据驿站。而\`readOnly\`配置，则在另一维度加固内存安全——若设为\`false\`，MyBatis将通过\`SerializedCache\`强制序列化/反序列化，避免多线程间对象引用导致的并发污染；若为\`true\`，则直接返回缓存对象副本，以空间换线程安全。每一项设置，都在\`Mapper\`的namespace疆域内，为缓存的内存驻留签下理性而克制的协议。 ## 五、二级缓存的使用场景与最佳实践 ### 5.1 适合使用二级缓存的业务场景 二级缓存并非万能钥匙，它的价值只在特定土壤中悄然生长——读多写少、数据变更频率低且对实时性要求不苛刻的业务场景，正是它最本真的栖息地。系统字典、地区编码、用户角色权限……这些如基石般稳固的基础数据，天然契合二级缓存的呼吸节奏：它们被高频查询，却极少更新；它们承载着业务逻辑的底层语义，却无需毫秒级同步。当多个SqlSession反复调用\`UserMapper.selectRoles()\`或\`DictMapper.getProvinceList()\`，二级缓存便在\`com.example.mapper.UserMapper\`与\`com.example.mapper.DictMapper\`各自的namespace疆域内，静默复用同一份结果，将数据库压力悄然卸下。这不是性能的炫技，而是对数据本质的尊重——有些信息生来就该被记住，只要它的世界尚未改变。 ### 5.2 二级缓存的性能优化技巧 性能从不诞生于盲目堆砌，而源于对机制的敬畏式雕琢。启用二级缓存后，真正的优化始于对\`\<cache>\`每一项属性的审慎落笔：将\`size\`从默认1024收敛至业务真实负载所需的256，既避免内存冗余，又迫使淘汰策略更早介入；设定\`flushInterval="60000"\`，让缓存每60秒主动归零一次，为可能滞留的陈旧数据划下明确的时间休止符；选用\`eviction="LRU"\`而非FIFO，则是将缓存命运交予访问热度——那些被遗忘最久的条目，理应让位于正在呼吸的数据。更关键的是，\`readOnly="true"\`的抉择，不是妥协，而是清醒：它允许MyBatis直接返回缓存对象引用，以空间换效率，在无并发修改风险的场景下，让每一次\`get\`都轻如羽落。这些配置，不是参数列表，而是开发者在namespace边界内，为缓存写下的理性契约。 ### 5.3 缓存穿透、缓存击穿的解决方案 缓存击穿的寒意，常在高并发瞬间刺破平静——当热点key失效，数十个线程同时扑向数据库，二级缓存的防线几近瓦解。MyBatis未提供银弹，却埋下了一颗克制的种子：\`BlockingCache\`。它不阻止失效，而是在失效发生时，让所有请求同一key的线程安静等待——仅放行一个去加载，其余共享结果。这并非魔法，而是以阻塞为代价，换取数据库的喘息。至于缓存穿透，MyBatis本身不拦截非法key，但其设计已预留出口：\`Cache\`接口的开放性，允许开发者注入自定义实现，在\`get\`前校验key合法性，或预设空对象占位。这些方案不喧哗，却紧扣“作用域限定在Mapper的namespace内”的铁律——所有防御，都发生在该namespace的缓存链路之内，绝不越界，亦不推诿。 ### 5.4 分布式环境下的二级缓存应用 MyBatis的二级缓存天然是单机的——它依赖\`PerpetualCache\`背后的\`ConcurrentHashMap\`，注定无法跨JVM实例共享。在分布式环境下，若强行沿用默认二级缓存，每个节点将维护一份独立副本，导致数据不一致与缓存污染。此时，MyBatis的可插拔设计显露出真正的分量：开发者可完全替换\`Cache\`实现，将\`\<cache type="com.example.cache.RedisCache"/>\`指向自定义的Redis-backed缓存类，使所有节点共享同一外部存储。但必须谨记——这种替换并未改变作用域本质：\`RedisCache\`仍绑定于单一Mapper的namespace，\`UserMapper\`与\`OrderMapper\`依旧各自独享缓存空间。分布式不是抹平边界，而是在更大尺度上，以技术为笔重绘namespace的疆域——让“跨SqlSession共享”升维为“跨节点共享”，却始终恪守“限定在Mapper的namespace内”这一不可撼动的原点。 ### 5.5 二级缓存常见问题排查与解决 当缓存命中率骤降，或出现诡异的\`NotSerializableException\`，问题往往藏在配置褶皱里。首要排查点，是全局开关\`cacheEnabled=true\`是否真正生效——若此配置关闭，所有Mapper层级的\`\<cache/>\`或\`@CacheNamespace\`都将形同虚设；其次，确认目标Mapper是否显式声明缓存，且\`namespace\`拼写零误差；再者，若启用了\`readOnly="false"\`，则必须确保所有被缓存的POJO类实现\`Serializable\`接口，否则\`SerializedCache\`将在反序列化时抛出异常。线程安全问题亦常源于误用：未启用\`SynchronizedCache\`装饰器却在高并发下读写同一缓存实例，或错误地将非线程安全的自定义\`Cache\`实现注入多个namespace。每一次排查，都是对MyBatis两级缓存协作逻辑的回溯——一级缓存是否先被绕过？\`CachingExecutor\`是否被正确代理？答案不在日志深处，而在\`SqlSession\`、\`Mapper\`与\`Cache\`三者环环相扣的源码路径之中。 ## 六、总结 二级缓存是MyBatis框架中一项关键的性能优化机制，其核心价值在于实现跨SqlSession的数据共享，且作用域严格限定在Mapper的namespace内——即所有属于同一Mapper接口或XML文件中的SQL语句共享同一个缓存实例。这一设计既保障了缓存隔离性，又赋予了按业务模块精细化治理的能力。通过\`Cache\`接口抽象、\`CachingExecutor\`统一调度、\`PerpetualCache\`底层承载及装饰器链式封装，MyBatis构建了一套可观察、可替换、可调试的缓存体系。配置上需兼顾全局开关\`cacheEnabled=true\`与局部声明（\`\<cache/>\`或\`@CacheNamespace\`），并审慎设定淘汰策略、大小、刷新周期与读写模式。其适用边界清晰：适用于读多写少、变更低频的基础数据场景；而在分布式环境或强一致性要求下，则需依托可插拔架构进行定制扩展。对开发者而言，深入理解其源码逻辑，方能在“性能提升”与“数据一致”之间作出理性权衡。

](https://www.showapi.com/news/article/6a7697b44ddd79ab67016c23)

*