springboot整合redis緩存一些知識點

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

前言 最近在做智能家居平臺,考慮到家居的控制需要快速的響應於是打算使用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。還有一些有關異常的處理。我也不是很懂具體可以參考:

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生成器

 

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


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

更多相關文章
  • 2019年11月8日,近期做項目開始實行前後端分離的方式開發,前端使用vue的框架,打包發佈後,調用後端介面出現跨域的問題,網上搜索出來的都是以下的配置方式: 但是,在我的項目中,按這種方式配置沒有效果,還會出現跨域的問題,後來發現是前後端請求設置的在Headers裡面傳輸token來進行校驗,那麼 ...
  • 網上看到很多人說 NPOI 的性能不行,自己寫了一個 NPOI 的擴展庫,於是想嘗試看看 NPOI 的性能究竟怎麼樣,道聽途說始終不如自己動手一試。 ...
  • 1. 沒有在Program里配置IIS webBuilder.UseIIS(); 2. StartupProduction 里AutoFac容器註入錯誤和新版的CORS中間件已經阻止使用允許任意Origin,即 AllowAnyOrgin設置了也不會生效 3. 可以嘗試下 在網站根目錄dotnet ...
  • 我是一名 ASP.NET 程式員,專註於 B/S 項目開發。累計文章閱讀量超過一千萬,我的博客主頁地址:https://www.itsvse.com/blog_xzz.html 網上有很多關於npoi讀取excel表格的例子,很多都是返回一個Datatable的對象,但是我需要的是一個list集合, ...
  • 場景 DevExpress的TreeList怎樣設置數據源,從實例入手: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/102548490 滑鼠雙擊TreeList中的某一節點,在雙擊事件中怎樣獲取當前節點。 註: 博客主頁: h ...
一周排行
  • static void Main(string[] args) { string url = "https://go.microsoft.com/fwlink/?linkid=2108895&amp;clcid=0x409"; DownloadBigFile(new Uri(url), "ssms.... ...
  • 1. 使用ASP.NET Core 3.x 構建 RESTful API - 1.準備工作 什麼是REST 什麼是REST REST一詞最早是在2000年,由Roy Fielding在他的博士論文《Architectural Styles and the Design of Network-base ...
  • wpf 兩個自定義控制項 一個是IP控制項,一個滑動條。先看下效果圖 IPControl 1、實際工作中有時需要設置IP信息,就想著做一個ip控制項。效果沒有window自帶的好,需要通過tab切換。但也能滿足使用。廢話不多說直接上代碼 IPControl.xaml IPControl.xaml.cs 2 ...
  • 近期和幾位做嵌入式開發的朋友閑聊過程中,一位朋友抱怨到:這C#太難用了,我想在N個窗體(或者是N個用戶組件之間)傳遞值都搞不定,非得要定義一個全局變數來存儲,然後用定時器來刷新值,太Low了。我急切的回答道:這很簡單,不就是委托的事嘛。那你來一個示例啊:朋友道。此為這篇博客的起因,所以此篇博客對於有 ...
  • 在面對對象編程中,類的三大特性分別為封裝,繼承,多態。其中多態的具體實現,依賴於三個方法,也就是虛方法,抽象類和介面。 多態的具體作用是什麼呢?或者說多態的存在有什麼意義呢?多態的存在有效的降低了程式的耦合度,在使用的時候,不僅可以表現大家都有的共性,還能在必要的時候突出一些特殊的的個性。 那麼如何 ...
  • [toc] 前言 在之前已經提到過,公用類庫Util已經開源,目的一是為了簡化開發的工作量,畢竟有些常規的功能類庫重覆率還是挺高的,二是為了一起探討學習軟體開發,用的人越多問題也就會越多,解決的問題越多功能也就越完善, 倉庫地址: "April.Util_github" , "April.Util_ ...
  • 【ASP.NET Core學習】在ASP.NET Core 種使用Entity Framework Core介紹,包括如何添加Entity Framwork Core,創建模型和遷移到資料庫,查詢數據,保存數據,使用事務,處理併發衝突 ...
  • 環境:MacOS 10.13 MAMAP Prophp 7.0.33 + xdebugVisual Studio Code前言我所理解的 POP Chain:利用魔術方法並巧妙構造特殊屬性調用一系列函數或類方法以執行某種敏感操作的調用堆棧反序列化常用魔法函數前言我所理解的 POP Chain:利用魔 ...
  • 當創建隊列jobs、監聽器或訂閱伺服器以推送到隊列中時,您可能會開始認為,一旦分派,隊列工作器決定如何處理您的邏輯就完全由您自己決定了。 嗯……並不是說你不能從作業內部與隊列工作器交互,但是通常情況下,哪怕你做了,也是沒必要的。 這個神奇的騷操作的出現是因為“InteractsWithQueue”這 ...
  • 在說正題之前先解釋一下交換機模式是個籠統的稱呼,它不是一個單獨的模式(包括了訂閱模式,路由模式和主題模式),交換機模式是一個比較常用的模式,主要是為了實現數據的同步。 首先,說一下訂閱模式,就和字面上的意思差不多主要就是一個生產者,多個消費者,同一個消息被多個消費者獲取,先看一下官網的圖示 整體執行 ...
x