基于Java實現(xiàn)Redis多級緩存方案
一、多級緩存
1. 傳統(tǒng)緩存方案
請求到達tomcat后,先去redis中獲取緩存,不命中則去mysql中獲取

2. 多級緩存方案
tomcat的請求并發(fā)數(shù),是遠小于redis的,因此tomcat會成為瓶頸- 利用請求處理每個環(huán)節(jié),分別添加緩存,減輕tomcat壓力,提升服務(wù)性能

二、JVM本地緩存
緩存是存儲在內(nèi)存中,數(shù)據(jù)讀取速度較快,能大量減少對數(shù)據(jù)庫的訪問,減少數(shù)據(jù)庫壓力
分布式緩存,如redis
- 優(yōu)點: 存儲容量大,可靠性好,可以在集群中共享
- 缺點: 訪問緩存有網(wǎng)絡(luò)開銷
- 場景: 緩存數(shù)據(jù)量大,可靠性高,需要在集群中共享的數(shù)據(jù)
進程本地緩存, 如HashMap, GuavaCache
- 優(yōu)點:讀取本地內(nèi)存,沒有網(wǎng)絡(luò)開銷,速度更快
- 缺點:存儲容量有限,可靠性低(如重啟后丟失),無法在集群中共享
- 場景:性能要求高,緩存數(shù)據(jù)量少
1. 實用案例
- Caffeine是一個基于java8開發(fā)的,提供了近乎最佳命中率的高性能的本地緩存庫
- 目前spring內(nèi)部的緩存用的就是這個
<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. 會將之前存儲的數(shù)據(jù)刪除掉*/
? ? ? ? cacheWithMaxPairs = Caffeine.newBuilder()
? ? ? ? ? ? ? ? .maximumSize(maxPairs)
? ? ? ? ? ? ? ? .build();
? ? }
? ? /*從緩存中獲取數(shù)據(jù)
? ? ?* 1. 如果緩存中有,則直接從緩存中返回
? ? ?* 2. 如果緩存中沒有,則去數(shù)據(jù)查詢并返回結(jié)果*/
? ? 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("數(shù)據(jù)庫查詢");
? ? ? ? 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 設(shè)置有效期
- 給緩存設(shè)置有效期,到期后自動刪除。再次查詢時可以更新
- 優(yōu)勢:簡單,方便
- 缺點:時效性差,緩存過期之前可能不一致
- 場景:更新頻率低,時效性要求比較低的業(yè)務(wù)
1.2 同步雙寫
- 在修改數(shù)據(jù)庫的同時,直接修改緩存
- 優(yōu)勢:有代碼侵入,緩存與數(shù)據(jù)庫強一致性
- 缺點:代碼進入,耦合性高
- 場景:對一致性,失效性要求較高的緩存數(shù)據(jù)
1.3 異步通知
- 修改數(shù)據(jù)庫時發(fā)送事件通知,相關(guān)服務(wù)監(jiān)聽到后修改緩存數(shù)據(jù)
- 優(yōu)勢:低耦合,可以同時通知多個緩存服務(wù)
- 缺點:時效性一把,可能存在緩存不一致問題
- 場景:時效性一般,有多個服務(wù)需要同步


2. 基于Canal的異步通知
- 是阿里旗下的一款開源項目,基于java開發(fā)
- 基于數(shù)據(jù)庫增量日志解析,提供增量數(shù)據(jù)訂閱和消費
- 基于mysql的主從備份的思想
2.1 mysql主從復(fù)制

2.2 canal 工作原理
- canal 模擬 MySQL slave 的交互協(xié)議,偽裝自己為 MySQL slave ,向 MySQL master 發(fā)送dump 協(xié)議
- MySQL master 收到 dump 請求, 開始推送 binary log 給 slave (即 canal )
- canal 解析 binary log 對象(原始為 byte 流)
到此這篇關(guān)于Redis多級緩存方案分享的文章就介紹到這了,更多相關(guān)Redis多級緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java搭建簡單Netty開發(fā)環(huán)境入門教程
這篇文章主要介紹了Java搭建簡單Netty開發(fā)環(huán)境入門教程,有詳細的代碼展示和maven依賴,能夠幫助你快速上手Netty開發(fā)框架,需要的朋友可以參考下2021-06-06
java字節(jié)碼框架ASM操作字節(jié)碼的方法淺析
這篇文章主要給大家介紹了關(guān)于java字節(jié)碼框架ASM如何操作字節(jié)碼的相關(guān)資料,文中通過示例代碼介紹的很詳細,有需要的朋友可以參考借鑒,下面來一起看看吧。2017-01-01
SpringBoot利用自定義json序列化器實現(xiàn)敏感字段數(shù)據(jù)脫敏詳解
這篇文章主要介紹了SpringBoot利用自定義json序列化器實現(xiàn)敏感字段數(shù)據(jù)脫敏詳解,因為案例代碼用到了hutool提供的DesensitizedUtil數(shù)據(jù)脫敏工具類,這里要引入hutool的依賴,如果你需要自定義 數(shù)據(jù)脫敏的邏輯,可以不引入這個依賴,需要的朋友可以參考下2024-01-01

