構建高可用二級緩存系統

二級緩存機制原理詳解

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;毅力是永久的…