news 2026/4/22 8:11:37

【12.MyBatis源码剖析与架构实战】10.2 ⼆级缓存存取流程剖析-案例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【12.MyBatis源码剖析与架构实战】10.2 ⼆级缓存存取流程剖析-案例

MyBatis 的缓存机制是其性能优化的关键模块之一,通过减少对数据库的直接访问来大幅提升查询效率。在深入二级缓存之前,理解整个缓存体系的基础(一级缓存)以及支撑它的核心源码至关重要。

🔍 缓存体系概览

MyBatis 提供两级缓存,均基于Cache接口实现。

  • 一级缓存 (Local Cache):默认开启且无法关闭,作用域为SqlSession级别。其本质是一个HashMap,执行相同的查询时,会先从该SqlSession的本地缓存中查找。
  • 二级缓存 (Global Cache):作用域为Mappernamespace级别,可跨SqlSession共享,需要手动配置开启。

🏗️ 核心源码剖析:Cache 接口与装饰器模式

org.apache.ibatis.cache.Cache是所有缓存实现的核心接口,定义了几个基本方法:

  • String getId():获取缓存对象的唯一标识符。
  • void putObject(Object key, Object value):将数据放入缓存。
  • Object getObject(Object key):从缓存中获取数据。
  • Object removeObject(Object key):从缓存中移除数据。
  • void clear():清空整个缓存。

MyBatis 的缓存模块大量运用了装饰器模式,通过层层包装为基础缓存PerpetualCache增加多样化的功能。

  • PerpetualCache(核心组件):最基础的缓存实现,内部直接使用HashMap存储键值对,是缓存模块的"核心"。
  • LruCache(装饰器):实现最近最少使用 (LRU)策略的缓存。它内部维护一个LinkedHashMap,并覆写removeEldestEntry方法,当缓存大小超过设定值 (setSize) 时,自动移除链表头部的元素(即最久未使用的条目)。
  • SynchronizedCache(装饰器):一个线程安全的缓存装饰器,其所有方法都使用synchronized关键字修饰,确保多线程环境下的数据一致性。
  • BlockingCache(装饰器):提供阻塞功能的缓存,用于防止缓存击穿。当一个线程在查询某个 Key 时,如果缓存未命中,该线程会获得这个 Key 的锁,并去数据库加载数据,其他线程必须等待,从而避免大量并发请求同时落到数据库。

此外,还有FifoCache(先进先出策略)和SoftCache/WeakCache(基于引用类型)等多种装饰器,可以根据需求灵活组合。

🔬 一级缓存源码分析 (以 SqlSession 为基础)

一级缓存的实现逻辑主要集中在org.apache.ibatis.executor.BaseExecutor类中。

  • 缓存存储:BaseExecutor维护了两个PerpetualCache实例:
    • localCache:用于缓存查询结果。
    • localOutputParameterCache:用于缓存存储过程的输出参数。
  • 缓存键: 一级缓存使用CacheKey对象作为键,以确保查询的精确匹配。CacheKey的构建涉及多个要素:
    1. MappedStatement 的 ID(namespace + id)。
    2. 查询结果的偏移量(RowBounds.offset)和限制条数(RowBounds.limit)。
    3. 具体的 SQL 语句(BoundSql.getSql())。
    4. SQL 语句中所有参数的实际值。
    5. 环境 ID(configuration.getEnvironment().getId())。
  • 执行流程 (BaseExecutor.query):
    // 代码逻辑简化自 org.apache.ibatis.executor.BaseExecutorpublic<E>List<E>query(MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler)throwsSQLException{// 1. 根据查询条件生成CacheKeyCacheKeykey=createCacheKey(ms,parameter,rowBounds,boundSql);// 2. 检查是否配置了在查询前清空一级缓存 (flushCacheRequested)if(ms.isFlushCacheRequired()){clearLocalCache();// 清空localCache}List<E>list=null;try{// 3. 尝试从一级缓存(localCache)中获取结果list=queryFromDatabase(ms,parameter,rowBounds,resultHandler,key,boundSql);}catch(SQLExceptione){throwe;}// ... 其他处理returnlist;}private<E>List<E>queryFromDatabase(...){List<E>list=(List<E>)localCache.getObject(key);if(list!=null){returnlist;// 缓存命中,直接返回}// 缓存未命中,执行数据库查询list=doQuery(ms,parameter,rowBounds,resultHandler,boundSql
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 8:10:14

Qt跨平台开发者的Windows API生存手册:如何安全传递动态内存指针?

Qt跨平台开发者的Windows API生存手册&#xff1a;动态内存指针的安全传递实践 1. 跨线程通信的本质挑战 在Windows平台进行跨线程开发时&#xff0c;动态内存管理始终是开发者需要面对的棘手问题。不同于Qt框架提供的信号槽机制自动处理内存生命周期&#xff0c;直接使用Windo…

作者头像 李华
网站建设 2026/4/11 18:49:44

“键盘鼠标”到“听懂人话”:如何用AI语音重构大屏交互新范式?

凌晨两点&#xff0c;某省级应急指挥中心的警报骤然响起。大屏上&#xff0c;红色预警信号在三维地图上闪烁——某山区突发暴雨&#xff0c;可能导致山体滑坡。值班员王磊没有像往常一样手忙脚乱地敲击键盘、拖动鼠标切换信号源&#xff0c;而是对着麦克风平静地说&#xff1a;…

作者头像 李华
网站建设 2026/4/11 18:49:17

微信小程序iOS操作系统BLE适配问题总结

微信小程序BLE&#xff08;低功耗蓝牙&#xff09;功能在iOS系统上的适配&#xff0c;受系统权限机制、微信版本差异、iOS系统版本迭代及蓝牙协议规范等多重因素影响&#xff0c;易出现连接异常、数据交互失败、页面卡顿等问题。本文结合实际开发场景与官方文档&#xff0c;梳理…

作者头像 李华
网站建设 2026/4/11 18:47:06

用nc命令模拟一个简单的TCP-UDP客户端和服务端

网络调试利器&#xff1a;nc命令实战指南 在网络编程和调试中&#xff0c;nc&#xff08;netcat&#xff09;被誉为“瑞士军刀”&#xff0c;它能快速模拟TCP/UDP客户端和服务端&#xff0c;无需编写代码即可完成网络测试。无论是端口扫描、数据传输&#xff0c;还是服务监听&…

作者头像 李华
网站建设 2026/4/11 18:44:24

Kd-tree在三维点云中的5个常见误区及解决方案

Kd-tree在三维点云中的5个常见误区及解决方案 当你在处理三维点云数据时&#xff0c;Kd-tree无疑是最常用的空间索引结构之一。它能够高效地组织海量点云数据&#xff0c;为近邻搜索、范围查询等操作提供加速。但就像任何强大的工具一样&#xff0c;如果使用不当&#xff0c;Kd…

作者头像 李华
网站建设 2026/4/11 18:42:59

如何高效备份QQ空间历史记录:GetQzonehistory实用工具全解析

如何高效备份QQ空间历史记录&#xff1a;GetQzonehistory实用工具全解析 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 想要永久保存QQ空间的青春回忆和珍贵时刻吗&#xff1f;GetQzon…

作者头像 李华