二级缓存机制原理详解

1. 整体架构

MyBatis-Plus二级缓存采用装饰器模式实现,核心组件包括:

  • Cache接口‌:定义缓存基本操作
  • PerpetualCache‌:基础缓存实现(HashMap)
  • 装饰器‌:如LruCache、FifoCache等
  • TransactionalCache‌:事务缓存管理器

2. 工作流程

  1. 初始化阶段‌:

    • 解析Mapper XML中的<cache>配置
    • 创建基础缓存实例
    • 根据配置添加装饰器
  2. 查询流程‌:

    sequenceDiagramparticipant Clientparticipant SqlSessionparticipant Executorparticipant Cacheparticipant DBClient->>SqlSession: 执行查询SqlSession->>Executor: query()Executor->>Cache: 检查缓存alt 缓存命中Cache-->>Executor: 返回缓存结果else 缓存未命中Executor->>DB: 执行查询DB-->>Executor: 返回结果Executor->>Cache: 缓存结果endExecutor-->>SqlSession: 返回结果SqlSession-->>Client: 返回结果
    

  3. 更新流程‌:

    sequenceDiagramparticipant Clientparticipant SqlSessionparticipant Executorparticipant Cacheparticipant DBClient->>SqlSession: 执行更新SqlSession->>Executor: update()Executor->>DB: 执行SQLDB-->>Executor: 返回影响行数Executor->>Cache: 清除相关缓存Executor-->>SqlSession: 返回结果SqlSession-->>Client: 返回结果
    

3. 关键实现细节

  • 缓存键生成‌:基于Mapper ID + 方法参数 + SQL + 分页等生成唯一键
  • 事务支持‌:通过TransactionalCacheManager管理事务提交/回滚时的缓存操作
  • 序列化‌:默认使用JVM序列化,可配置为其他序列化方式

生产案例详细实现步骤

案例1:电商商品缓存系统

  1. 基础配置
<!-- mybatis-config.xml -->
<configuration><settings><setting name="cacheEnabled" value="true"/><!-- 配置缓存序列化方式 --><setting name="defaultCacheType" value="com.example.MyCustomCache"/></settings>
</configuration>
  1. Mapper配置
<!-- ProductMapper.xml -->
<mapper namespace="com.example.mapper.ProductMapper"><cache type="org.mybatis.caches.redis.RedisCache"eviction="LRU"flushInterval="300000"size="1024"readOnly="false"/><select id="selectById" resultType="Product" useCache="true">SELECT * FROM product WHERE id = #{id}</select>
</mapper>
  1. 服务层实现
@Service
public class ProductServiceImpl implements ProductService {@Autowiredprivate ProductMapper productMapper;// 带缓存穿透保护的查询public Product getProductWithCache(Long id) {// 1. 先查缓存Product product = productMapper.selectById(id);if (product != null) {return product;}// 2. 缓存不存在,查数据库product = productMapper.selectFromDb(id);if (product == null) {// 防止缓存穿透:缓存空对象product = new Product();product.setId(id);product.setName("NULL_OBJECT");productMapper.cacheNullObject(product);} else {// 放入缓存productMapper.cacheProduct(product);}return product;}@Transactionalpublic void updateProduct(Product product) {// 1. 更新数据库productMapper.updateById(product);// 2. 清除缓存productMapper.clearCache(product.getId());// 3. 异步重建缓存CompletableFuture.runAsync(() -> {Product freshProduct = productMapper.selectFromDb(product.getId());productMapper.cacheProduct(freshProduct);});}
}
  1. 自定义Redis缓存实现
public class CustomRedisCache implements Cache {private final String id;private final RedisTemplate<String, Object> redisTemplate;public CustomRedisCache(String id) {this.id = id;this.redisTemplate = SpringContextHolder.getBean("redisTemplate");}@Overridepublic String getId() {return this.id;}@Overridepublic void putObject(Object key, Object value) {// 自定义序列化逻辑redisTemplate.opsForValue().set(generateRedisKey(key), serialize(value),30, TimeUnit.MINUTES // 设置TTL);}// 其他方法实现...
}

案例2:多级缓存策略

  1. 配置多级缓存
@Configuration
public class CacheConfig {@Beanpublic Cache productCache() {// 一级缓存:本地缓存(Caffeine)CaffeineCache localCache = new CaffeineCache("localProductCache",Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(5, TimeUnit.MINUTES).build());// 二级缓存:Redis缓存RedisCache redisCache = new RedisCache("redisProductCache");// 构建多级缓存return new MultiLevelCache(localCache, redisCache);}
}
  1. 多级缓存实现
public class MultiLevelCache implements Cache {private final Cache[] caches;public MultiLevelCache(Cache... caches) {this.caches = caches;}@Overridepublic Object getObject(Object key) {// 按顺序查询缓存for (Cache cache : caches) {Object value = cache.getObject(key);if (value != null) {// 填充上级缓存for (Cache upperCache : getUpperCaches(cache)) {upperCache.putObject(key, value);}return value;}}return null;}// 其他方法实现...
}

生产环境注意事项

  1. 缓存一致性解决方案‌:

    • 使用消息队列实现缓存更新
    @RabbitListener(queues = "cache.update.queue")
    public void handleCacheUpdate(CacheUpdateMessage message) {if (message.getType().equals("PRODUCT")) {productMapper.clearCache(message.getId());}
    }
    

  2. 监控指标采集‌:

public class MonitoredCache implements Cache {private final Cache delegate;private final CacheMetrics metrics;@Overridepublic Object getObject(Object key) {long start = System.currentTimeMillis();try {Object value = delegate.getObject(key);metrics.recordHit(value != null);metrics.recordLatency(System.currentTimeMillis() - start);return value;} catch (Exception e) {metrics.recordError();throw e;}}// 其他方法...
}
  1. 缓存预热策略‌:
@PostConstruct
public void preloadHotProducts() {List<Long> hotProductIds = productMapper.selectHotProductIds();hotProductIds.parallelStream().forEach(id -> {Product product = productMapper.selectById(id);// 主动放入缓存productMapper.cacheProduct(product);});
}

通过以上详细实现,可以构建一个高性能、高可用的二级缓存系统,适用于各种生产环境场景。

下面是springboot用法:


<?xml version="1.0" encoding="UTF-8"?>
<project><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.4</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies>
</project>spring:datasource:url: jdbc:mysql://localhost:3306/testusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverredis:host: localhostport: 6379password: database: 0mybatis-plus:configuration:cache-enabled: true@Configuration
public class RedisCacheConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());return template;}@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));return RedisCacheManager.builder(factory).cacheDefaults(config).transactionAware().build();}
}@CacheNamespace(implementation = RedisCache.class, eviction = RedisCache.class)
public interface UserMapper extends BaseMapper<User> {@Options(useCache = true)@Select("SELECT * FROM user WHERE id = #{id}")User selectUserById(Long id);@CacheEvict@Update("UPDATE user SET name=#{name} WHERE id=#{id}")int updateUserName(@Param("id") Long id, @Param("name") String name);
}@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Cacheable(key = "#id", unless = "#result == null")public User getUserById(Long id) {return userMapper.selectById(id);}@Transactional@CacheEvict(key = "#user.id")public void updateUser(User user) {userMapper.updateById(user);}@CacheEvict(allEntries = true)public void clearAllCache() {// 清空所有缓存}
}@Configuration
@MapperScan("com.example.mapper")
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}@Beanpublic ConfigurationCustomizer configurationCustomizer() {return configuration -> {configuration.setCacheEnabled(true);configuration.setLocalCacheScope(LocalCacheScope.SESSION);};}
}
  1. @CacheNamespace(MyBatis注解)
  • 作用:在Mapper接口级别声明启用二级缓存
  • 核心属性:
    • implementation:指定自定义缓存实现类(默认PerpetualCache)
    • eviction:指定缓存淘汰策略(LRU/FIFO等)
    • flushInterval:缓存刷新间隔(毫秒)
    • size:缓存最大容量
  • 示例:

javaCopy Code

@CacheNamespace(implementation = RedisCache.class, eviction = FifoCache.class, flushInterval = 60000) public interface UserMapper {...}

  1. @Options(MyBatis注解)
  • 作用:为单个SQL语句提供额外配置选项
  • 常用属性:
    • useCache:是否使用二级缓存(默认true)
    • flushCache:执行后是否清空缓存(默认false)
    • timeout:查询超时时间(秒)
  • 示例:

javaCopy Code

@Options(useCache = true, flushCache = false, timeout = 10) @Select("SELECT * FROM users WHERE id = #{id}") User findById(Long id);

  1. @CacheEvict(Spring缓存注解)
  • 作用:方法执行后清除指定缓存
  • 关键属性:
    • value/cacheNames:目标缓存名称
    • key:要清除的缓存键(支持SpEL)
    • allEntries:是否清空整个缓存区域
    • beforeInvocation:是否在方法执行前清除
  • 典型使用场景:

javaCopy Code

@CacheEvict(value = "userCache", key = "#user.id") public void updateUser(User user) { // 更新操作后会清除userCache中该用户的缓存 }

三者关系示意图:

  1. @CacheNamespace定义Mapper的缓存策略
  2. @Options控制单个SQL语句的缓存行为
  3. @CacheEvict在Service层维护缓存一致性

生产建议:

  1. 对于读写频繁的数据,建议组合使用@CacheEvict@Cacheable
  2. 分布式环境建议使用Redis等集中式缓存实现
  3. 注意设置合理的缓存过期时间防止脏数据

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

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

相关文章

MacOS微信双开,亲测有效

本机配置打开终端运行以下命令 第一步&#xff1a;sudo cp -R /Applications/WeChat.app /Applications/WeChat2.app第二步&#xff1a;sudo /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.tencent.xinWeChat2" /Applications/WeChat2.app/Contents/Info…

Drupal XSS漏洞复现:原理详解+环境搭建+渗透实践(CVE-2019-6341)

目录 一、Drupal XSS漏洞 二、环境搭建 1、确保系统已安装 Docker 和 Docker-Compose 2、下载 Vulhub 3、进入漏洞环境 4、启动漏洞环境 5、查看环境状态 6、初始化Drupal环境 &#xff08;1&#xff09;访问 Drupal 安装页面 &#xff08;2&#xff09;完成图形化安…

Redis复制延迟全解析:从毫秒到秒级的优化实战指南

Redis主从延迟飙升导致数据不一致&#xff1f;订单丢失、缓存穿透频发&#xff1f;本文深入剖析8大复制延迟元凶&#xff0c;并提供解决方案&#xff0c;让你的复制延迟从秒级降到毫秒级&#xff01; 一、复制延迟:分布式系统的隐形杀手 ⚠️ 什么是复制延迟&#xff1f; 当主…

数据预处理与特征工程全流程指南:数据清洗、缺失值填补、类别特征编码、特征缩放归一化、特征选择与降维(PCA/LDA)实战解析

1. 数据预处理与特征工程 “数据清洗、缺失值填补、类别特征编码、特征缩放/归一化、特征选择与降维&#xff08;PCA、LDA&#xff09;” 等流程&#xff0c;虽然被反复提到&#xff0c;但要在复杂的实际数据集中一步步落实&#xff0c;难度很大。 摘要 在机器学习与深度学习…

小迪安全v2023学习笔记(七十九讲)—— 中间件安全IISApacheTomcatNginxCVE

文章目录前记服务攻防——第七十九天中间件安全&IIS&Apache&Tomcat&Nginx&弱口令&不安全配置&CVE中间件 - IIS-短文件&解析&蓝屏等中间件 - Nginx-文件解析&命令执行等常见漏洞介绍漏洞复现中间件 - Apache-RCE&目录遍历&文件…

《云原生微服务治理进阶:隐性风险根除与全链路能力构建》

云原生微服务架构已成为企业支撑业务快速迭代的核心载体&#xff0c;但治理能力的滞后却常常成为制约发展的短板。许多企业在完成服务容器化、部署自动化后&#xff0c;便陷入了“架构先进但治理粗放”的困境—服务数量激增导致依赖关系失控&#xff0c;流量波动加剧引发资源配…

因泰立科技:用激光雷达重塑智能工厂物流生态

应对AGV碰撞困局——激光雷达如何重塑智能工厂物流在现代化工厂中&#xff0c;AGV&#xff08;自动引导车&#xff09;与移动机器人已成为提升生产效率的重要设备。然而&#xff0c;这些智能设备在忙碌的工厂环境中&#xff0c;却时常面临碰撞的困扰。这不仅影响了生产效率&…

Remove.bg:精准自动抠图工具,高效解决图片去背景需求

有没有做设计时被 “抠图去背景” 搞到崩溃的朋友&#xff1f;上次小索奇帮同事做活动海报&#xff0c;要把人物图从复杂背景里抠出来&#xff0c;用 PS 选了半天钢笔工具&#xff0c;头发丝还是漏一块缺一块&#xff0c;折腾半小时都没弄好 —— 后来被设计圈的朋友安利了 Rem…

功率器件固晶挑战:抗高温翘曲治具提升IGBT焊接强度30%

功率器件封装新突破&#xff1a;耐高温防变形工装助力IGBT模块焊接强度提升30%在功率半导体封装领域&#xff0c;IGBT模块的芯片贴装工艺正面临严峻挑战。随着功率密度不断提升&#xff0c;传统固晶工装在高温环境下易发生形变&#xff0c;导致焊接层产生空洞、裂纹等缺陷。最新…

MATLAB可以实现的各种智能算法

MATLAB可以实现的各种智能算法(包括但不限于)中文名称英文全称缩写出现年份算法来由&#xff08;灵感来源&#xff09;遗传算法Genetic AlgorithmGA1975模拟达尔文生物进化论的自然选择和遗传学机理&#xff08;选择、交叉、变异&#xff09;粒子群优化算法Particle Swarm Opti…

【iOS】push,pop和present,dismiss

文章目录前言push和poppresent和dismiss基本方法属性说明常见的用法运行演示push和present区别前言 在之前的学习中&#xff0c;我们发现iOS有两种用于推出新界面的常用方法&#xff0c;分别是push和present&#xff0c;但是二者存在很多区别 present只能返回自己的上一级视图…

编写后端JAR包蓝绿发布脚本

前端发布脚本的功能 保留每一个发布版本&#xff0c;防止新版本异常&#xff0c;方便回撤用户无感知&#xff0c;防止发布过程中的宕机 原理&#xff1a; 发布的JAR包只是一个软连接&#xff0c;新的JAR启动正常后&#xff0c;切换软连接指向即可。蓝绿JAR包绑定端口不同&…

Kafka 和 RabbitMQ的选择

h5打开以查看 选择 Kafka 还是 RabbitMQ&#xff08;或其他传统消息队列&#xff09;并不是一个谁比谁更好的问题&#xff0c;而是 “哪种工具更适合你的特定场景” 的问题。 它们的设计哲学、核心架构和目标用例有根本性的不同。简单来说&#xff1a; RabbitMQ 是一个消息代理…

LeetCode 刷题【66. 加一、67. 二进制求和】

66. 加一 自己做 解&#xff1a;逢十进位 class Solution { public:vector<int> plusOne(vector<int>& digits) {int add 1; //进位vector<int> res(digits.size() 1); //防止多一位的情况&#xff1a;9 1 10for(int i (int)digits…

《MATLAB 批量把振动 CSV(含中文“序号/采样频率”)稳健转成 .mat:自动解析+统一换算+按 H/I/O/F-rpm-fs-load 命名》

一文搞定&#xff1a;批量把中文头信息的 CSV&#xff08;含“序号/采样频率”等&#xff09;稳健转成 .mat&#xff0c;并按规则重命名&#xff08;H/I/O/F-rpm-fs-load&#xff09;1. 项目背景 在振动/故障诊断采集里&#xff0c;我们经常得到一批 CSV 文件&#xff0c;文件名…

Unity与硬件交互终极指南:从Arduino到自定义USB设备

这篇教程将遵循“是什么-为什么-怎么做”的结构,从宏观思路讲起,再深入到具体的技术实现,并附上可以直接使用的详细代码和注释,确保不同水平的读者都能从中受益。 前言:当虚拟照进现实 在游戏开发和交互设计的世界里,我们常常满足于键盘、鼠标和手柄。但当你想要创造真正…

Tomcat 日志文件名的命名规范

1. 核心日志类型及命名规范 (1) Catalina 日志&#xff08;引擎级日志&#xff09; 文件名&#xff1a;catalina.<yyyy-MM-dd>.log说明&#xff1a;记录 Tomcat 启动、关闭、严重错误等全局事件。示例&#xff1a;catalina.2025-09-07.log (2) Access 日志&#xff08;访…

.vsdx文件转pdf、word、ppt等文件在线分享(免费版)

网址如下&#xff1a; https://www.easeconvert.com/visio-to-pdf/ 只需要把.vsdx文件放到网址中即可

如何在Docker容器之间进行通信?

目录 1. 使用 Docker 网络(推荐) 步骤: 特点: 2. 端口映射(主机中转) 步骤: 特点: 3. 使用 link(不推荐,已过时) 4. 跨主机容器通信 总结 在 Docker 中,容器之间的通信可以通过多种方式实现,选择哪种方式取决于你的具体需求(如网络隔离性、跨主机通信等…

从 “Hello AI” 到企业级应用:Spring AI 如何重塑 Java 生态的 AI 开发

&#x1f525;个人主页&#xff1a;草莓熊Lotso &#x1f3ac;作者简介&#xff1a;C研发方向学习者 &#x1f4d6;个人专栏&#xff1a; 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》 ⭐️人生格言&#xff1a;生活是默默的坚持&#xff0c;毅力是永久的…