最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Spring Cache使用Redisson分布式鎖解決緩存擊穿問題

 更新時間:2022年04月24日 10:36:44   作者:dreaming9420  
本文主要介紹了詳解Spring Cache使用Redisson分布式鎖解決緩存擊穿問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

1 什么是緩存擊穿

一份熱點數(shù)據(jù),它的訪問量非常大。在其緩存失效的瞬間,大量請求直達存儲層,導致服務(wù)崩潰。

2 為什么要使用分布式鎖

在項目中,當共享資源出現(xiàn)競爭情況的時候,為了防止出現(xiàn)并發(fā)問題,我們一般會采用鎖機制來控制。在單機環(huán)境下,可以使用synchronized或Lock來實現(xiàn);但是在分布式系統(tǒng)中,因為競爭的線程可能不在同一個節(jié)點上(同一個jvm中),所以需要一個讓所有進程都能訪問到的鎖來實現(xiàn),比如mysql、redis、zookeeper。

3 什么是Redisson

Redisson是一個在Redis的基礎(chǔ)上實現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對象,還實現(xiàn)了可重入鎖(Reentrant Lock)、公平鎖(Fair Lock、聯(lián)鎖(MultiLock)、 紅鎖(RedLock)、 讀寫鎖(ReadWriteLock)等,還提供了許多分布式服務(wù)。Redisson提供了使用Redis的最簡單和最便捷的方法。Redisson的宗旨是促進使用者對Redis的關(guān)注分離(Separation of Concern),從而讓使用者能夠?qū)⒕Ω械胤旁谔幚順I(yè)務(wù)邏輯上。

4 Spring Boot集成Redisson

4.1 添加maven依賴

不再需要spring-boot-starter-data-redis依賴,但是都添加也不會報錯

<!--redisson-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.17.0</version>
</dependency>

4.2 配置yml

spring:
  datasource:
    username: xx
    password: xxxxxx
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=CTT
  cache:
    type: redis
  redis:
    database: 0
    port: 6379               # Redis服務(wù)器連接端口
    host: localhost          # Redis服務(wù)器地址
    password: xxxxxx         # Redis服務(wù)器連接密碼(默認為空)
    timeout: 5000            # 超時時間

4.3 配置RedissonConfig

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
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.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.Random;

@EnableCaching
@Configuration
public class RedissonConfig {

? ? @Value("${spring.redis.host}")
? ? private String host;

? ? @Value("${spring.redis.port}")
? ? private String port;

? ? @Value("${spring.redis.password}")
? ? private String password;


? ? @Bean(destroyMethod = "shutdown") ?// bean銷毀時關(guān)閉Redisson實例,但不關(guān)閉Redis服務(wù)
? ? public RedissonClient redisson() {
? ? ? ? //創(chuàng)建配置
? ? ? ? Config config = new Config();
? ? ? ? /**
? ? ? ? ?* ?連接哨兵:config.useSentinelServers().setMasterName("myMaster").addSentinelAddress()
? ? ? ? ?* ?連接集群: config.useClusterServers().addNodeAddress()
? ? ? ? ?*/
? ? ? ? config.useSingleServer()
? ? ? ? ? ? ? ? .setAddress("redis://" + host + ":" + port)
? ? ? ? ? ? ? ? .setPassword(password)
? ? ? ? ? ? ? ? .setTimeout(5000);
? ? ? ? //根據(jù)config創(chuàng)建出RedissonClient實例
? ? ? ? return Redisson.create(config);
? ? }

? ? @Bean
? ? public CacheManager RedisCacheManager(RedisConnectionFactory factory) {
? ? ? ? RedisSerializer<String> redisSerializer = new StringRedisSerializer();
? ? ? ? Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
? ? ? ? // 解決查詢緩存轉(zhuǎn)換異常的問題
? ? ? ? ObjectMapper om = new ObjectMapper();
? ? ? ? om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
? ? ? ? /**
? ? ? ? ?* 新版本中om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)已經(jīng)被廢棄
? ? ? ? ?* 建議替換為om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL)
? ? ? ? ?*/
? ? ? ? om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
? ? ? ? jackson2JsonRedisSerializer.setObjectMapper(om);
? ? ? ? // 配置序列化解決亂碼的問題
? ? ? ? RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
? ? ? ? ? ? ? ? // 設(shè)置緩存過期時間 ?為解決緩存雪崩,所以將過期時間加隨機值
? ? ? ? ? ? ? ? .entryTtl(Duration.ofSeconds(60 * 60 + new Random().nextInt(60 * 10)))
? ? ? ? ? ? ? ? // 設(shè)置key的序列化方式
? ? ? ? ? ? ? ? .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
? ? ? ? ? ? ? ? // 設(shè)置value的序列化方式
? ? ? ? ? ? ? ? .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
? ? ? ? // .disableCachingNullValues(); //為防止緩存擊穿,所以允許緩存null值
? ? ? ? RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
? ? ? ? ? ? ? ? .cacheDefaults(config)
? ? ? ? ? ? ? ? // 啟用RedisCache以將緩存 put/evict 操作與正在進行的 Spring 管理的事務(wù)同步
? ? ? ? ? ? ? ? .transactionAware()
? ? ? ? ? ? ? ? .build();
? ? ? ? return cacheManager;
? ? }
}

5 使用Redisson的分布式鎖解決緩存擊穿

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.company.dubbodemo.entity.User;
import com.company.dubbodemo.mapper.UserMapper;
import com.company.dubbodemo.service.UserService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
? ? ? ? implements UserService {
? ??
? ? @Resource
? ? private RedissonClient redissonClient;

? ? @Resource
? ? private UserMapper userMapper;

? ? @Override
? ? // 一定要設(shè)置sync = true開啟異步,否則會導致多個線程同時獲取到鎖
? ? @Cacheable(cacheNames = "user", key = "#id", sync = true)
? ? public User findById(Long id) {
? ? ? ? /**
? ? ? ? ?*
? ? ? ? ?* 加了@Cacheable之后方法體執(zhí)行說明緩存中不存在所查詢的數(shù)據(jù)
? ? ? ? ?* 獲取一把鎖,只要鎖的名字一樣,就是同一把鎖
? ? ? ? ?*/

? ? ? ? /**
? ? ? ? ?* 注意: 如果設(shè)置了lock.lock(10,TimeUnit.SECONDS) 鎖過期不會自動續(xù)期
? ? ? ? ?* ? ? ?1、如果我們傳遞了鎖的過期時間,就發(fā)送給redis執(zhí)行腳本,進行占鎖,默認超時就是我們指定的時間
? ? ? ? ?* ? ? ?2、如果沒有指定鎖的超時時間,就使用30000L(LockWatchdogTimeout 看門狗的默認時間)
? ? ? ? ?* ? ? ?可通過RedissonConfig-->getRedissonClient()-->config.setLockWatchdogTimeout()設(shè)置看門狗時間
? ? ? ? ?* ? ? ? ? 只要占鎖成功就會啟動一個定時任務(wù)【就會重新給鎖設(shè)置過期時間,新的時間就是看門狗的默認時間】,每隔10s都會自動續(xù)期,續(xù)期成30s
? ? ? ? ?* 看門狗機制
? ? ? ? ?* 1、鎖的自動續(xù)期,如果業(yè)務(wù)超長,運行期間自動給鎖續(xù)上新的30s。不用擔心因為業(yè)務(wù)時間長,鎖自動過期被刪除
? ? ? ? ?* 2、加鎖的業(yè)務(wù)只要運行完成,就不會給當前鎖續(xù)期,即使不手動解鎖,鎖默認在30s以后自動刪除
? ? ? ? ?*
? ? ? ? ?*/
? ? ? ? RLock lock = redissonClient.getLock("redissonClient-lock");

? ? ? ? // 對第一個線程執(zhí)行方法體的線程加鎖,加了@Cacheable,方法執(zhí)行之后會將方法的返回值存入緩存,下一個線程直接讀取緩存
? ? ? ? lock.lock();
? ? ? ? User user = new User();
? ? ? ? try {
? ? ? ? ? ? user = userMapper.selectById(id);
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? lock.unlock();
? ? ? ? }
? ? ? ? return user;
? ? }
}

到此這篇關(guān)于詳解Spring Cache使用Redisson分布式鎖解決緩存擊穿問題的文章就介紹到這了,更多相關(guān)Spring Cache 緩存擊穿內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

乾安县| 漳州市| 防城港市| 郁南县| 商城县| 化德县| 吉安县| 建瓯市| 利津县| 观塘区| 西青区| 渑池县| 扎囊县| 绍兴市| 巴塘县| 郁南县| 商城县| 高青县| 长丰县| 汉川市| 宣威市| 南宫市| 将乐县| 抚顺县| 上犹县| 泰兴市| 玉田县| 卢湾区| 武清区| 晋宁县| 禹城市| 乐都县| 兴海县| 连州市| 达州市| 六盘水市| 峨边| 肥乡县| 始兴县| 南城县| 南涧|