springboot整合redis緩存一些知識點

前言

最近在作智能家居平臺,考慮到家居的控制須要快速的響應因而打算使用redis緩存。一方面減小數據庫壓力另外一方面又能提升響應速度。項目中使用的技術棧基本上都是你們熟悉的springboot全家桶,在springboot2.x之後操做redis的客戶端推薦使用lettuce(生菜)取代jedis。git

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

1、配置文件

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

說明:redis

  • spring.cache.type: redis

已經代表使用項目採用redis作爲緩存方式。spring

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

表示是否緩存空值,通常狀況下是容許的。由於這涉及到緩存的三大問題:緩存穿透、緩存雪崩、緩存擊穿。數據庫

若是設置false即不容許緩存空值,這樣會致使不少請求數據庫沒有的數據時,不會緩存到redis致使每次都會請求到數據庫。這種狀況即:緩存穿透。json

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

2、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. 類上的註解@EnableCachingspringboot

代表開啓緩存功能。app

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是沒法讀取、刪除的。
 
 

3、緩存註解使用

@Cacheable 使用在查詢方法上

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

@CacheEvict 使用在刪除方法上

須要注意的是@Cacheable@CachePut方法必定要有返回被緩存對象。由於註解使用的AOP切面若是沒有返回值表示緩存對象爲空值。

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

 

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

相關文章
相關標籤/搜索