首頁 > 軟體

基於Java實現Redis多級快取方案

2022-03-22 19:00:06

一、多級快取

1. 傳統快取方案

請求到達tomcat後,先去redis中獲取快取,不命中則去mysql中獲取

2. 多級快取方案

  • tomcat的請求並行數,是遠小於redis的,因此tomcat會成為瓶頸
  • 利用請求處理每個環節,分別新增快取,減輕tomcat壓力,提升服務效能

二、JVM本地快取

快取是儲存在記憶體中,資料讀取速度較快,能大量減少對資料庫的存取,減少資料庫壓力

分散式快取,如redis
 - 優點: 儲存容量大,可靠性好,可以在叢集中共用
 - 缺點: 存取快取有網路開銷
 - 場景: 快取資料量大,可靠性高,需要在叢集中共用的資料

程序本地快取, 如HashMap, GuavaCache
- 優點:讀取本地記憶體,沒有網路開銷,速度更快
- 缺點:儲存容量有限,可靠性低(如重啟後丟失),無法在叢集中共用
- 場景:效能要求高,快取資料量少

1. 實用案例

  • Caffeine是一個基於java8開發的,提供了近乎最佳命中率的高效能的本地快取庫
  • 目前spring內部的快取用的就是這個
<dependency>
     <groupId>com.github.ben-manes.caffeine</groupId>
     <artifactId>caffeine</artifactId>
     <version>3.0.5</version>
 </dependency>
package com.erick.cache;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.time.Duration;

public final class CacheUtil {
    private static int expireSeconds = 2;
    public static Cache<String, String> cacheWithExpireSeconds;

    private static int maxPairs = 1;
    public static Cache<String, String> cacheWithMaxPairs;

    static {
        /*過期策略,寫完60s後過期*/
        cacheWithExpireSeconds = Caffeine.newBuilder()
                .expireAfterWrite(Duration.ofSeconds(expireSeconds))
                .build();

        /*過期策略,達到最大值後刪除
         * 1. 並不會立即刪除,等一會兒才會刪除
         * 2. 會將之前儲存的資料刪除掉*/
        cacheWithMaxPairs = Caffeine.newBuilder()
                .maximumSize(maxPairs)
                .build();
    }

    /*從快取中獲取資料
     * 1. 如果快取中有,則直接從快取中返回
     * 2. 如果快取中沒有,則去資料查詢並返回結果*/
    public static String getKeyWithExpire(String key) {
        return cacheWithExpireSeconds.get(key, value -> {
            return getResultFromDB();
        });
    }

    public static String getKeyWithMaxPair(String key) {
        return cacheWithMaxPairs.get(key, value -> {
            return getResultFromDB();
        });
    }

    private static String getResultFromDB() {
        System.out.println("資料庫查詢");
        return "db result";
    }
}
package com.erick.cache;

import java.util.concurrent.TimeUnit;

public class Test {

    @org.junit.Test
    public void test01() throws InterruptedException {
        CacheUtil.cacheWithExpireSeconds.put("name", "erick");
        System.out.println(CacheUtil.getKeyWithExpire("name"));
        TimeUnit.SECONDS.sleep(3);
        System.out.println(CacheUtil.getKeyWithExpire("name"));
    }

    @org.junit.Test
    public void test02() throws InterruptedException {
        CacheUtil.cacheWithMaxPairs.put("name", "erick");
        CacheUtil.cacheWithMaxPairs.put("age", "12");

        System.out.println(CacheUtil.getKeyWithMaxPair("name"));
        System.out.println(CacheUtil.getKeyWithMaxPair("age"));

        TimeUnit.SECONDS.sleep(2);

        System.out.println(CacheUtil.getKeyWithMaxPair("name")); // 查詢不到了
        System.out.println(CacheUtil.getKeyWithMaxPair("age"));
    }
}

三、快取一致性

1. 常見方案

1.1 設定有效期

  • 給快取設定有效期,到期後自動刪除。再次查詢時可以更新
  • 優勢:簡單,方便
  • 缺點:時效性差,快取過期之前可能不一致
  • 場景:更新頻率低,時效性要求比較低的業務

1.2 同步雙寫

  • 在修改資料庫的同時,直接修改快取
  • 優勢:有程式碼侵入,快取與資料庫強一致性
  • 缺點:程式碼進入,耦合性高
  • 場景:對一致性,失效性要求較高的快取資料

1.3 非同步通知

  • 修改資料庫時傳送事件通知,相關服務監聽到後修改快取資料
  • 優勢:低耦合,可以同時通知多個快取服務
  • 缺點:時效性一把,可能存在快取不一致問題
  • 場景:時效性一般,有多個服務需要同步

2. 基於Canal的非同步通知

  • 是阿里旗下的一款開源專案,基於java開發
  • 基於資料庫增量紀錄檔解析,提供增量資料訂閱和消費
  • 基於mysql的主從備份的思想

2.1 mysql主從複製

2.2 canal 工作原理

  • canal 模擬 MySQL slave 的互動協定,偽裝自己為 MySQL slave ,向 MySQL master 傳送dump 協定
  • MySQL master 收到 dump 請求, 開始推播 binary log 給 slave (即 canal )
  • canal 解析 binary log 物件(原始為 byte 流)

到此這篇關於Redis多級快取方案分享的文章就介紹到這了,更多相關Redis多級快取內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com