MyBatis缓存架构深度拆解:从PerpetualCache的LRU陷阱到Redis分布式二级缓存防穿透实战 - 详解

news/2025/10/2 18:46:31/文章来源:https://www.cnblogs.com/ljbguanli/p/19123867

MyBatis缓存架构深度拆解:从PerpetualCache的LRU陷阱到Redis分布式二级缓存防穿透实战 - 详解

一、开场白:你们要的“缓存避坑指南”,我攒了三年经验

做后端开发这八年,我见过太多人栽在MyBatis缓存上——
有人一级缓存不关,导致多线程串数据;
有人二级缓存乱用,缓存穿透把DB干崩;
更离谱的是,有人连PerpetualCache的LRU策略都没搞懂,就敢说自己“精通”MyBatis。

上周帮某电商团队排查接口QPS暴跌问题,根源就是缓存穿透:黑客批量刷不存在的商品ID,一级缓存不存null,二级缓存默认不开,DB直接被怼到5000+ QPS。

今晚不藏着掖着,把我踩过的坑、调过的参数、踩过的雷,全掏出来——PerpetualCache的底层LRU逻辑,到用Redis搭分布式二级缓存防穿透,手把手教你把MyBatis缓存用成“数据库保镖”​

二、MyBatis缓存底层:一级是“本地小仓库”,二级是“分布式粮仓”

先掰扯清楚两个核心概念——这不是文档复述,是我看了MyBatis源码、踩过无数坑后的总结。

1. 一级缓存(Local Cache):SqlSession的“临时抽屉”

  • 默认开​:每个SqlSession自带,生命周期随Session结束(比如Service方法跑完就清空);
  • ​**底层是PerpetualCache**​:用LinkedHashMap实现LRU(最近最少使用)淘汰;
  • 坑点​:不存null值(怕脏数据)、多实例不共享(实例1查过的商品,实例2还得撞DB)。

源码级真相​:
PerpetualCachecache字段是个LinkedHashMap,构造时accessOrder=true——每次get都会把Entry移到链表尾(标记“最近用过”);超过容量(默认无界,但可通过size限制)时,删链表头的“最老”Entry。

// MyBatis源码简化:PerpetualCache的核心存储结构
public class PerpetualCache implements Cache {private final Map cache = new LinkedHashMap(128, 0.75F, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry eldest) {return size() > maxSize; // 超容量,删最老的}};// ... get/put方法直接操作这个map
}

2. 二级缓存(Second Level Cache):Namespace的“分布式粮仓”

  • 跨实例共享​:Mapper.xmlnamespace级别生效(比如所有UserMapper的查询共享缓存);
  • 默认关​:需手动开<cache/>标签;
  • 可扩展​:能替换成Redis、Ehcache等分布式实现——这才是解决多实例缓存共享的关键。

三、缓存穿透:查不存在的数据,为什么能把DB搞崩?

回到电商团队的故障——黑客刷不存在的商品ID(如-1)​,这就是典型的缓存穿透:

  • 定义​:查询数据库中不存在的数据,缓存无记录,每次请求都打DB;
  • 危害​:批量攻击时,DB连接池耗尽,接口响应从200ms飙到5s;
  • MyBatis的盲区​:一级缓存不存null,二级缓存默认也不存——等于“放任”无效查询直达数据库。

三级缓存穿透解决方案:从“堵”到“防”

方案1:缓存空对象(Null Object)——简单粗暴但有效
  • 做法​:DB查不到数据时,缓存(key, null),并设短过期时间(如5分钟);
  • 优点​:挡住重复无效查询;
  • 缺点​:占缓存空间,数据新增后需等过期。

MyBatis配置​(Mapper.xml中开启nullValue):

方案2:布隆过滤器——100%拦截“绝对不存在”的key
  • 做法​:初始化时把所有存在的商品ID灌进布隆过滤器;
  • 逻辑​:查询前先过布隆过滤器——
    • 若返回“不存在”,直接返回空,不查缓存和DB;
    • 若返回“可能存在”,再查二级缓存。

实战注意​:布隆过滤器有误判率(“可能存在”的key可能实际不存在),需结合空对象兜底。

方案3:接口层校验——拦截明显无效请求
  • 做法​:Controller层对参数做合法性校验(如商品ID必须是正整数);
  • 优点​:拦截低级错误(如ID=-1、字符串);
  • 缺点​:防不住“看起来合法”但不存在的ID(如ID=99999999)。

四、实战:用Redis搭MyBatis分布式二级缓存——从0到1

一级缓存解决单实例问题,二级缓存解决多实例共享——Redis是最优解,分布式、高可用、支持灵活序列化。

1. 环境准备

  • 依赖​(Spring Boot项目):

org.mybatis.cachesmybatis-redis1.0.0-beta2

org.springframework.bootspring-boot-starter-data-redis

Redis配置​(application.yml):

spring:redis:host: 127.0.0.1port: 6379password:database: 0

2. 配置MyBatis使用Redis二级缓存

Mapper.xml中指定Redis实现,关键参数:

  • expire:缓存过期时间(防脏数据);
  • nullValue:是否缓存null(防穿透);
  • readWrite:是否读写缓存(默认true


3. 自定义Redis Cache(优化Key和序列化)

默认的RedisCache用JDK序列化,Key是namespace::id,不够友好。自定义Cache更灵活:

public class CustomRedisCache implements Cache {private final RedisTemplate redisTemplate;private final String namespace;public CustomRedisCache(String namespace) {this.namespace = namespace;this.redisTemplate = SpringContextUtils.getBean("redisTemplate");}@Overridepublic String getId() {return namespace; // 缓存唯一标识}@Overridepublic void putObject(Object key, Object value) {// 自定义Key:namespace + 方法名 + 参数(如userMapper.selectById::1)String cacheKey = String.format("%s:%s:%s", namespace, "selectById", key);redisTemplate.opsForValue().set(cacheKey, value, 5, TimeUnit.MINUTES); // 设过期时间}@Overridepublic Object getObject(Object key) {String cacheKey = String.format("%s:%s:%s", namespace, "selectById", key);return redisTemplate.opsForValue().get(cacheKey);}// 其他方法(remove/clear等)按需实现...
}

Mapper.xml指定自定义Cache​:

. 测试验证

  • 正常查询​:第一次查商品1,查DB存Redis;第二次直接从Redis拿;
  • 不存在的商品​:查ID=-1,DB返回空,Redis存(-1, null),5分钟后过期;
  • 黑客攻击​:刷不存在的ID,后续请求都从Redis拿空值,DB QPS稳定在50+。

五、避坑指南:Redis二级缓存的“生死线”

  1. 缓存一致性​:更新DB时,必须同步删Redis缓存(比如@Update后调用redisTemplate.delete(cacheKey));
  2. 序列化​:推荐Jackson/Fastjson(比JDK序列化小30%、快2倍);
  3. 过期时间​:根据业务调整(商品详情5分钟,用户信息10分钟);
  4. 集群模式​:所有实例连同一Redis集群,避免缓存分裂。

六、结语:缓存是工具,用对了才是“数据库保镖”

这场“缓存救火”让我彻底明白:

  • 一级缓存是“本地优化”,解决单实例重复查询;
  • 二级缓存是“分布式扩展”,解决多实例资源共享;
  • 缓存穿透不是缓存的问题,是“没正确用缓存”的问题——空对象、布隆过滤器、接口校验,三管齐下才能防住。

最后送你MyBatis缓存终极Checklist

  1. 开二级缓存前,确认“需要跨实例共享”;
  2. 必须缓存null值,避免无效查询直达DB;
  3. Redis自定义Key和序列化,别用默认的;
  4. 更新数据时,同步清除缓存。

附录:资料包

  • MyBatis Redis缓存源码:mybatis-redis;
  • Guava布隆过滤器:BloomFilter;
  • Redis序列化工具:Jackson2JsonRedisSerializer。

(全文完)

作者​:踩过MyBatis所有缓存坑的后端老炮——专注用“实战经验”帮你少走弯路。

福利​:关注我,私信“MyBatis缓存”,送你《Redis分布式二级缓存配置手册》+《布隆过滤器实战代码》。

关键词​:MyBatis一级缓存、二级缓存、LRU淘汰、缓存穿透、Redis分布式缓存

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/925196.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025柔版印刷机厂家 TOP 企业品牌推荐排行榜,塑编袋,编织袋,阀口袋,重包膜,机组式,卫星式,不换版,FFS 重载膜,水泥袋柔版印刷机公司推荐!

在当前包装印刷行业快速发展的背景下,柔版印刷机作为关键生产设备,其市场需求持续增长,但同时行业也面临着诸多亟待解决的问题。一方面,随着下游行业对印刷精度、速度及环保要求的不断提高,部分传统柔版印刷机厂家…

免费北京网站建设济南 制作网站 公司吗

我喜欢回答各种各样的问题&#xff0c;自然也喜欢记录下自己的一些观点和看法。希望给朋友们多一点参考&#xff0c;也欢迎交流探讨。 提问&#xff1a; 自考本科&#xff0c;学的开发语言&#xff0c;问互联网行业求职和发展&#xff01; 作为一个资深码农&#xff0c;对这样…

做网站公司哪家便宜WordPress的固态链接

行首: Ctrl A 行末:Ctrl E转载于:https://www.cnblogs.com/summer1019/p/11043692.html

蒙古文网站建设工作情况汇报宁晋网站建设代理价格

在当今科技飞速发展的时代&#xff0c;鸿蒙系统以其独特的微内核架构和对人工智能算法的深度融合&#xff0c;正引领着操作系统智能化的新潮流。本文将深入探讨鸿蒙系统的微内核架构是如何与人工智能算法高效协同&#xff0c;从而提升系统性能和智能化水平的。 鸿蒙系统微内核…

西安网站关键词优化虚拟主机网站500错误

文章来源&#xff1a;https://medium.com/voxel51/how-to-cluster-images-6e09bdff7361 2024 年 4 月 10 日 使用 FiftyOne、Scikit-learn和特征嵌入 在 2024 年深度学习的计算密集型环境中&#xff0c;集群一词最常出现在讨论 GPU 集群时--高度优化的矩阵乘法机器的大规模集…

9 30 -

9 30P2194很显然的强连通分量P4168考虑分块,预处理出每种颜色在每个整块中的出现次数,定义 \(p_{l,r}\) 为在第 \(l\) 块到第 \(r\) 块中出现次数最多的颜色 可以发现可以做到 \(O(N \sqrt{N})\)下午vp mx模拟赛 两个…

2025/10/2

A 用时:2h 预期:55pts 实际:35pts 写了超大常熟 \(k\sqrt{R}\) 的做法,没算时间复杂度以为 Sub 2 能过,实际上 T 飞了,直接预处理因数个数就能过。 总结:写完一定要算时间复杂度,不要想当然以为能过。 B 用时:…

网站开发顶岗周记现在做网络推广好做吗

&#x1f388;写在前面 &#x1f64b;‍♂️大家好呀&#xff0c;我是超梦。小伙伴们都知道&#xff0c;不管是在学习中还是日常工作中&#xff0c;几乎天天是要跟数据库打交道的&#xff0c;为了更好的操作数据库&#xff0c;我们的SQL知识储备是必不可少的。想要掌握好SQL&am…

Spring 基础核心 - SpringMVC 入门与请求流程

Spring 基础核心 - SpringMVC 入门与请求流程本文是介绍 Spring 技术的第四篇入门级文章,前面已经介绍了 Spring 入门案例、IOC 容本文是介绍 Spring 技术的第四篇入门级文章,前面已经介绍了 Spring 入门案例、IOC 容…

网络平台怎么建立网站吗汕头百度搜索排名优化

张宇、汤家凤、武忠祥、李永乐、杨超、王式安、方浩这些老师都有自己擅长的细分 比如张宇老师&#xff0c;杨超&#xff0c;汤家凤&#xff0c;武忠祥老师的高数讲的很好&#xff0c;李永乐老师是线代的神&#xff0c;王式安、方浩概率论讲的很好&#xff0c;所以对于不同的学…

(数据结构)链表OJ——刷题练习 - 实践

(数据结构)链表OJ——刷题练习 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mo…

重测序数据fastp数据质控及fastQC质量评估

001、fastp -i sample_name_1.fq.gz -o sample_name_qc_1P.fastq.gz -I sample_name_2.fq.gz -O sample_name_qc_2P.fastq.gz --thread 4 -g -q 20 -u 30 -l 150 --overlap_diff_limit 1 --overlap_diff_percent_limit…

网站建设公司南京wordpress 数据库说明

一.递归 &#xff08;1&#xff09;汉诺塔问题 当n2时&#xff0c;要将A中最下面盘子上方的盘子放到B上&#xff0c;最下面盘子放到C上&#xff0c;再将B上的盘子通过A放到C即可&#xff1b; 当n3时&#xff0c;要将A中最下面盘子上方的盘子放到B上&#xff0c;最下面盘子放到…

请问怎么做网站惠州网站开发

类的加载顺序 有父子关系的类在加载时先调用父类静态初始化块&#xff0c;静态属性&#xff0c;但不包括静态方法&#xff0c;然后再是&#xff0c;子类静态初始化块&#xff0c;静态属性&#xff0c;但同样不包括静态方法 。 类什么时候被加载/类加载时机&#xff1a; 第一&…

深圳网站建设机构深圳工程建设交易服务中心网站

Java基础教程之多线程 下 &#x1f539;本节学习目标1️⃣ 线程的同步与死锁1.1 同步问题的引出2.2 synchronized 同步操作2.3 死锁 2️⃣ 多线程经典案例——生产者与消费者&#x1f50d;分析sleep()和wait()的区别&#xff1f; &#x1f33e; 总结 &#x1f539;本节学习目标…

东莞做网站的公司乐山高端网站建设

现在&#xff0c; 电视机 对于每个家庭来说已然不是什么奢侈品&#xff0c;并且已经成为必备的家庭电器之一。其实&#xff0c;国产电视机中也有不少的知名品牌&#xff0c;但是许多消费者对国外的电子技术持有更高的认可度&#xff0c;因此下面小编就来跟大家讲讲sony电视&…

企业网站建设大概费用网站打开速度突然变慢的原因

... 转载于:https://www.cnblogs.com/2008nmj/p/7264769.html

8. Spring AI tools/function-call - 教程

8. Spring AI tools/function-call - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

企业网站优化方案范本收费网站必须备案吗

RHEL6的网络管理与RHEL5的有比较大的改变。虽然在RHEL5、6中均安装有NetworkManager&#xff0c;在RHEL5中2、3、4、5级别中默认是不启用的。但在RHEL6中&#xff0c;默认是启用的&#xff0c;NetworkManager会一直监控网卡状态&#xff0c;修改网卡参数立即生效不用重启服务。…