springboot整合redis緩存一些知識點

来源:https://www.cnblogs.com/backnow/archive/2019/11/09/11824061.html
-Advertisement-
Play Games

前言 最近在做智能家居平臺,考慮到家居的控制需要快速的響應於是打算使用redis緩存。一方面減少資料庫壓力另一方面又能提高響應速度。項目中使用的技術棧基本上都是大家熟悉的springboot全家桶,在springboot2.x以後操作redis的客戶端推薦使用lettuce(生菜)取代jedis。 ...


前言

最近在做智能家居平臺,考慮到家居的控制需要快速的響應於是打算使用redis緩存。一方面減少資料庫壓力另一方面又能提高響應速度。項目中使用的技術棧基本上都是大家熟悉的springboot全家桶,在springboot2.x以後操作redis的客戶端推薦使用lettuce(生菜)取代jedis。

jedis的劣勢主要在於直連redis,又無法做到彈性收縮。

一、配置文件

application.yml文件中的內容

spring:
  application:
    name: simple-lettuce
  cache:
    type: redis
    redis:
      # 緩存超時時間ms
      time-to-live: 60000
      # 是否緩存空值
      cache-null-values: true
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456
    # 連接超時時間(毫秒)
    timeout: 60000
    # Redis預設情況下有16個分片,這裡配置具體使用的分片,預設是0
    database: 1
    # spring2.x redis client 採用了lettuce(生菜),放棄使用jedis
    lettuce:
      # 關閉超時時間
      shutdown-timeout: 30000
      pool:
        # 連接池最大連接數(使用負值表示沒有限制) 預設 8
        max-active: 30
        # 連接池最大阻塞等待時間(使用負值表示沒有限制) 預設 -1
        max-wait: -1
        # 連接池中的最大空閑連接 預設 8
        max-idle: 8
        # 連接池中的最小空閑連接 預設 0
        min-idle: 0

說明:

  • spring.cache.type: redis

已經表明使用項目採用redis做為緩存方式。

  • spring.cache.redis.cache-null-values: true

表示是否緩存空值,一般情況下是允許的。因為這涉及到緩存的三大問題:緩存穿透、緩存雪崩、緩存擊穿。

如果設置false即不允許緩存空值,這樣會導致很多請求資料庫沒有的數據時,不會緩存到redis導致每次都會請求到資料庫。這種情況即:緩存穿透。

具體想初步瞭解這些概念可以參考文章:緩存三大問題及解決方案!

二、config配置類


@Configuration
@EnableCaching
public class RedisTemplateConfig extends CachingConfigurerSupport {

    private static Map<String, RedisCacheConfiguration> cacheMap = Maps.newHashMap();

    @Bean(name = "stringRedisTemplate")
    @ConditionalOnMissingBean(name = "stringRedisTemplate") //表示:如果容器已經有redisTemplate bean就不再註入
    public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory redisConnectionFactory) {return new StringRedisTemplate(redisConnectionFactory);
    }

    @Bean(name = "redisTemplate")
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        System.out.println("RedisTemplateConfig.RedisTemplate");
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // key的序列化採用StringRedisSerializer
        template.setKeySerializer(keySerializer());
        template.setHashKeySerializer(keySerializer());
        // value值的序列化採用fastJsonRedisSerializer
        template.setValueSerializer(valueSerializer()); //使用fastjson序列化
        template.setHashValueSerializer(valueSerializer()); //使用fastjson序列化
        template.setConnectionFactory(lettuceConnectionFactory);
        return template;
    }

    /**
     * 添加自定義緩存異常處理
     * 當緩存讀寫異常時,忽略異常
     * 參考:https://blog.csdn.net/sz85850597/article/details/89301331
     */
    @Override
    public CacheErrorHandler errorHandler() {
        return new IgnoreCacheErrorHandler();
    }

    @SuppressWarnings("Duplicates")
    @Bean
    @Primary//當有多個管理器的時候,必須使用該註解在一個管理器上註釋:表示該管理器為預設的管理器
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 預設配置
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(keyPair())
                .serializeValuesWith(valuePair())
                .entryTtl(Duration.ofSeconds(DEFAULT_TTL_SECS)) //設置過期時間
                .disableCachingNullValues();

        // 其它配置
        for(MyCaches cache : MyCaches.values()) {
            cacheMap.put(cache.name(),
                    RedisCacheConfiguration.defaultCacheConfig()
                            .serializeKeysWith(keyPair())
                            .serializeValuesWith(valuePair())
                            .entryTtl(cache.getTtl())
                            // .disableCachingNullValues() // 表示不允許緩存空值
                            .disableKeyPrefix() // 不使用預設首碼
                    // .prefixKeysWith("mytest") // 添加自定義首碼
            );
        }

        /** 遍歷MyCaches添加緩存配置*/
        RedisCacheManager cacheManager = RedisCacheManager.builder(
                RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory)
        )
                .cacheDefaults(defaultCacheConfig)
                .withInitialCacheConfigurations(cacheMap)
                .transactionAware()
                .build();

        ParserConfig.getGlobalInstance().addAccept("mypackage.db.entity.");
        return cacheManager;
    }

    /**
     * key序列化方式
     * @return
     */
    private RedisSerializationContext.SerializationPair<String> keyPair() {
        RedisSerializationContext.SerializationPair<String> keyPair =
                RedisSerializationContext.SerializationPair.fromSerializer(keySerializer());
        return keyPair;
    }

    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    /**
     * value序列化方式
     * @return
     */
    private RedisSerializationContext.SerializationPair<Object> valuePair() {
        RedisSerializationContext.SerializationPair<Object> valuePair =
                RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer());
        return valuePair;
    }

    /**
     * 使用fastjson序列化
     * @return
     */
    private RedisSerializer<Object> valueSerializer() {
        MyFastJsonRedisSerializer<Object> fastJsonRedisSerializer = new MyFastJsonRedisSerializer<>(Object.class);
        return fastJsonRedisSerializer;
    }

    @Getter
    private enum MyCaches {
        defaultCache(Duration.ofDays(1)),
        MyCaches(Duration.ofMinutes(10));

        MyCaches(Duration ttl) {
            this.ttl = ttl;
        }
        /** 失效時間 */
        private Duration ttl = Duration.ofHours(1);
    }
}

 說明

1. 類上的註解@EnableCaching

表明開啟緩存功能。

2. extends CachingConfigurerSupport

這個類就很豐富了,其實如果沒有什麼特別操作也可以不用繼承這個類。

這個類可以支持動態選擇緩存方式,比如項目中不止一種緩存方案,有可能有ehcache那麼可以自定義在什麼情況下使用redis使用情況下使用ehcache。還有一些有關異常的處理。我也不是很懂具體可以參考:

springboot(25)自定義緩存讀寫機制CachingConfigurerSupport

3. StringRedisTemplate和RedisTemplate的使用

(1)兩者的主要差別是:如果你只想緩存簡單的字元串選擇StringRedisTemplate是一個明智的舉措。如果想使用redis緩存一些對象數據肯定是要選擇RedisTemplate。   (2)RedisTemplate需要註意一點就是要怎麼選擇序列化工具。預設使用jdk的序列化緩存數據後即value值是無法直接閱讀的而存的二進位數據。 通常我們會選擇jackson或者fastjson來序列化對象,把對象轉換成json格式。兩者序列化對象後都會在頭部加上一個對象類路徑如:@type com.mypackage.entity.User。這個也算是一種安全策略。 比如使用fastjosn就會在cacheManager中指定序列化對象的包所在位置白名單:ParserConfig.getGlobalInstance().addAccept("mypackage.db.entity."); fastjson官方說明:https://github.com/alibaba/fastjson/wiki/enable_autotype   (3)還有需要註意如果value是string類型。RedisTemplate會在字元串外圍再加一對雙引號,如""abc""。如果使用RedisTemplate讀取則能得到abc,但是我在項目使用Jedis讀取就成了"abc"這就導致這些字元串無法被反序列化。   (4)StringRedisTemplate和RedisTemplate兩者數據是相互隔離的,如果使用StringRedisTemplate存入的數據使用RedisTemplate是無法讀取、刪除的。    

三、緩存註解使用

@Cacheable 使用在查詢方法上

@CachePut 使用在更新、保存方法上

@CacheEvict 使用在刪除方法上

需要註意的是@Cacheable@CachePut方法一定要有返回被緩存對象。因為註解使用的AOP切麵如果沒有返回值表示緩存對象為空值。

@CacheConfig註解在類上,可以選擇使用哪個緩存、緩存管理器、Key生成器

 

好了以上就是最近在項目中的一些知識點總結,如果以後使用緩存有新的體會我會同步更新的。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、阻塞隊列:用於保存等待執行的任務。在阻塞隊列中,線程阻塞的兩種情況: 1.當隊列中沒有數據的情況下,消費者端的所有線程都會被自動阻塞(掛起),直到有數據放入隊列。 2.當隊列中填滿數據的情況下, 生產者端的所有線程都會被自動阻塞,知道隊列中有空位置,線程被自動喚醒。 二、阻塞隊列的主要方法 拋出 ...
  • 一、實習內容 選擇一個調度演算法,實現處理器調度。 二、實習目的 在採用多道程式設計的系統中,往往有若幹個進程同時處於就緒狀態。當就緒進程個數大於處理器數時,就必須依照某種策略來決定哪些進程優先占用處理器。本實習模擬在單處理器情況下的處理器調度,幫助學生加深瞭解處理器調度的工作。 三、實習題目 本實習 ...
  • Python實戰教程,用Python做列印日曆的小程式,使用者可以通過輸入年月信息,程式將會輸出這個月的日曆 ...
  • 首先來總結一下,ArrayList的一些特點: 1.arraylist本質上就是一個elementData數組,它允許對元素進行快速隨機訪問,可以存放null值; 2.arraylist區別於數組的地方在於能夠自動擴展大小,其中關鍵就是grow() 方法,每次擴充後數組為原來數組的1.5倍; 3.a ...
  • 前言 array,顧名思義,數組,就是存儲數字、處理數字的一種數據結構。今天在將list轉換為array時,遇到了一個問題,數據量比較大,剛開始怎麼都不知道問題出在哪裡。直到我用一個3 3的小數據測試時,才發現問題的本質所在。浪費了半天的時間,不過總算搞明白了。 學的不夠踏實,以此警戒所有的初學者: ...
  • Python 3最重要的新特性之一是對字元串和二進位數據流做了明確的區分。文本總是Unicode,由str類型表示,二進位數據則由bytes類型表示。Python 3不會以任意隱式的方式混用str和bytes,你不能拼接字元串和位元組流,也無法在位元組流里搜索字元串(反之亦然),也不能將字元串傳入參數為 ...
  • Python編寫類的時候,每個函數參數第一個參數都是self,一開始我不管它到底是幹嘛的,只知道必須要寫上。後來對Python漸漸熟悉了一點,再回頭看self的概念,似乎有點弄明白了。 首先明確的是self只有在類的方法中才會有,獨立的函數或方法是不必帶有self的。self在定義類的方法時是必須有 ...
  • php中有很多排序的函數,sort,rsort,ksort,krsort,asort,arsort,natcasesort,這些函數用來對數組的鍵或值進行這樣,或那樣的排序。 可以終究有時候還需要一些函數來隨機獲取數組的元素。 array_rand()函數 隨機獲取數組中的一個函數,可以通過第二個參 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...