高級-06Redisson(分布式鎖)和SpringCache(緩存)


一、Redisson

1、入門配置

引入依賴

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>

配置文件

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class MyRedissonConfig {

    /**
     * 所有對Redisson的是用都是通過RedissonClient對象
     **/
    @Bean(destroyMethod="shutdown")
    public RedissonClient redisson() throws IOException {
        Config config = new Config();
        // redisson集群模式
//        config.useClusterServers()
//                .addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");
        // redisson單節點模式
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        // 根據config創建出RedissonClient實例
        return Redisson.create(config);
    }

}

2、可重入鎖-看門狗

2.1 默認加鎖時間

2.2 手動加鎖

lock.lock(10, TimeUnit.SECONDS); // 加鎖以后10秒鍾自動解鎖

手動加鎖時,不會自動續期,自動解鎖時間一定要大於業務的執行時間!!!

  • 1、如果傳遞了鎖的超時時間,源碼中發送給redis一個執行腳本,進行占鎖,默認超時就是指定的時間
  • 2、如果未指定鎖的超時時間,就是用30 * 1000【lockWatchdogTimeout看門狗的默認時間】;只要占鎖成功,就會啟動一個定時任務【重新給鎖設置多起事件,新的過期時間就是看門狗的默認時間】,每隔10s都會自動再次續期,續成30s滿時間。internalLockLeaseTime【看門狗時間】 / 3,10s

2.3 最佳實戰

盡量使用

lock.lock(30, TimeUnit.SECONDS); 

省掉了整個續期操作,手動解鎖。

超時時間設置30秒。如果業務操作30s還沒返回---說明業務已經完蛋了

3、讀寫鎖

保證一定能讀到最新數據,修改期間,寫鎖是一個排他鎖(互斥鎖、獨享鎖)。讀鎖是一個共享鎖
寫鎖沒釋放讀就必須等待
寫 + 讀:等待寫鎖釋放
寫 + 寫:阻塞方式
讀 + 寫:寫鎖需要等待
讀 + 讀:相當於無鎖,並發讀,只會在redis中記錄好所有當前讀鎖。它們都會同時加鎖成功。

!!!只要有寫的地方,都必須等待!!!

4、信號量

應用:停車位、限流

5、閉鎖

二、Spring Cache

1、簡介

  • Spring從3.1開始定義了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接囗來統一不同的緩存技術;並支持使用JCache(JSR107)注解簡化我們開發;
  • Cache接囗為緩存的組件規范定義,包含緩存的各種操作集合;
    Cache接囗下 Spring 提供了各種 xxxCach的實現;如 RedisCache,EhCacheCache,ConcurrentMapCache等;
  • 每次調用需要緩存功能的方法時,Spring會檢查指定參數的指定的目標方法是否已經被調用過;如果有就直接從緩存中獲取方法調用后的結果,如果沒有就調用方法並緩存結果后返回給用戶。下次調用直接從緩存中獲取。
  • 使用 Spring 緩存抽象時我們需要關注以下兩點;
    1、確定方法需要被緩存以及他們的緩存策略
    2、從緩存中讀取之前緩存存儲的數據

2、基礎概念

3、SpringBoot整合Cache

3.1 引入依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 使用redis作為緩存的話也需要引入redis依賴 -->

3.2 配置

(1)自動配置

CacheAutoConfiguration會導入 RedisCacheConfiguration

RedisCacheConfiguration 自動配置好了緩存管理器 RedisCacheManager

(2)手動配置

spring:
  cache:
    type: redis
#    cache-names: name1,name2,name3

3.3 開啟緩存功能

@EnableCaching
@SpringBootApplication
public class Application {
	...

4、緩存注解

4.1 @Cacheable

讀模式下設置緩存

// 簡單使用
@Cacheable({"name1","name2"})
public Object getData (){
    // 獲取業務數據
    return data;
}

1、每一個需要緩存的數據我們都需要指定放到哪個名字的緩存。【緩存分區(按業務類型分)】
2、@Cacheable代表當前方法的結果需要緩存
如果緩存中有,則不需要調用方法
如果緩存中沒有,會調用方法查詢結果並放入緩存
3、默認行為
1)、如果緩存中有,方法不調用
2)、key默認自動生成 【緩存名字】::SimpleKey[](自主生成的key值)
3)、緩存的value值,默認使用jdk序列化機制,將序列化的數據存到redis
4)、默認ttl時間 -1(即永不過期)

4.2 自定義緩存配置

1、指定緩存使用的key

key屬性支持SpEL表達式,普通字符串記得加單引號

@Cacheable(value = {"name1"}, key = "'level1Categorys'")

2、指定緩存數據存活時間

spring:
  cache:
    redis:
      time-to-live: 1000  ## 毫秒為單位
      # 開啟緩存空值,防止緩存穿透
      cache-null-values: true
      # 開啟緩存前綴,不設置的情況下默認使用分區名為前綴
      use-key-prefix: true

3、數據保存為json格式

import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

// 2、@ConfigurationProperties(prefix = "spring.cache") -> CacheProperties
// 原本配置文件綁定的配置類是上面這樣的。沒有放在容器中
// 開啟屬性綁定功能,綁定CacheProperties屬性類
// 這樣就能把綁定CacheProperties屬性類放進spring容器中
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
@EnableCaching
public class MyCacheConfig {

    @Bean // 3、可以直接設置參數為spring容器中的bean,從而獲取CacheProperties.Redis
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        // 1、使用自定義配置的話會無法獲取配置文件中的屬性
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        // 4、使配置文件中的屬性生效
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }
}

4.3 @CacheEvict

寫模式下的-->緩存失效模式

// 更新操作后刪除緩存
@CacheEvict(value = {"name1"}, key = "'level1Categorys'")
@Transaction
public void update(){
    // do something...
}

// 刪除某個分區下的所有數據
// 約定:存儲同一類型的數據,都可以指定成同一個分區,以后好刪除
@CacheEvict(value = {"name1"}, allEntries = true)
@Transaction
public void update(){
    // do something...
}

4.4 @Caching

同時操作多個緩存

@Caching(evict = {
        @CacheEvict(value = {"name1"}, key = "'Categorys-01'"),
        @CacheEvict(value = {"name1"}, key = "'Categorys-02'")
})
@Transaction
public void update(){
    // do something...
}

4.5 @CachePut

方法完成后將數據寫入緩存

適用於-緩存雙寫模式

5、Spring Cache原理與不足

5.1 原理

CacheManager(RedisCacheManager)->Cache(RedisCache)->Cache負責緩存的讀寫

5.2 讀模式

  • 緩存穿透:大量查詢一個null的數據。解決:緩存空數據 cache-null-values: true
  • 緩存擊穿:高並發同時查詢過期數據。解決:cache默認讀不加鎖,可以設置讀鎖 @Cacheable(value = "", sync = true)
  • 緩存雪崩:大量key同時過期。解決:加隨機時間;其實只要加上過期時間就可以滿足要求 time-to-live: 1000

5.3 寫模式

  • 讀寫加鎖。(讀多寫少)
  • 引入Canal,感知到Mysql的更新去更新緩存
  • 讀多寫多時,直接去數據庫查詢就行

5.4 總結

常規數據(讀多寫少,即時性、一致性要求不高的數據):完全可以使用Spring-Cache;寫模式(只要緩存的數據有過期時間就足夠了)

特殊數據:特殊設計


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM