欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

1 什么是緩存擊穿

一份熱點(diǎn)數(shù)據(jù),它的訪問量非常大。在其緩存失效的瞬間,大量請(qǐng)求直達(dá)存儲(chǔ)層,導(dǎo)致服務(wù)崩潰。

2 為什么要使用分布式鎖

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

3 什么是Redisson

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

4 Spring Boot集成Redisson

4.1 添加maven依賴

不再需要spring-boot-starter-data-redis依賴,但是都添加也不會(huì)報(bào)錯(cuò)

<!--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ù)器連接密碼(默認(rèn)為空)
    timeout: 5000            # 超時(shí)時(shí)間

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銷毀時(shí)關(guān)閉Redisson實(shí)例,但不關(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實(shí)例
? ? ? ? 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è)置緩存過期時(shí)間 ?為解決緩存雪崩,所以將過期時(shí)間加隨機(jī)值
? ? ? ? ? ? ? ? .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 操作與正在進(jìn)行的 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開啟異步,否則會(huì)導(dǎo)致多個(gè)線程同時(shí)獲取到鎖
? ? @Cacheable(cacheNames = "user", key = "#id", sync = true)
? ? public User findById(Long id) {
? ? ? ? /**
? ? ? ? ?*
? ? ? ? ?* 加了@Cacheable之后方法體執(zhí)行說明緩存中不存在所查詢的數(shù)據(jù)
? ? ? ? ?* 獲取一把鎖,只要鎖的名字一樣,就是同一把鎖
? ? ? ? ?*/

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

? ? ? ? // 對(duì)第一個(gè)線程執(zhí)行方法體的線程加鎖,加了@Cacheable,方法執(zhí)行之后會(huì)將方法的返回值存入緩存,下一個(gè)線程直接讀取緩存
? ? ? ? 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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot中關(guān)于static和templates的注意事項(xiàng)以及webjars的配置

    SpringBoot中關(guān)于static和templates的注意事項(xiàng)以及webjars的配置

    今天小編就為大家分享一篇關(guān)于SpringBoot中關(guān)于static和templates的注意事項(xiàng)以及webjars的配置,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-01-01
  • 帶你快速搞定java多線程(3)

    帶你快速搞定java多線程(3)

    這篇文章主要介紹了java多線程編程實(shí)例,分享了幾則多線程的實(shí)例代碼,具有一定參考價(jià)值,加深多線程編程的理解還是很有幫助的,需要的朋友可以參考下
    2021-07-07
  • Java并發(fā)程序入門介紹

    Java并發(fā)程序入門介紹

    這篇文章主要介紹了Java并發(fā)程序入門 ,需要的朋友可以參考下
    2015-03-03
  • Java語(yǔ)言中的文件數(shù)據(jù)流示例詳解

    Java語(yǔ)言中的文件數(shù)據(jù)流示例詳解

    這篇文章主要為大家介紹了Java語(yǔ)言中的文件數(shù)據(jù)流示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Java 實(shí)戰(zhàn)項(xiàng)目錘煉之網(wǎng)上花店商城的實(shí)現(xiàn)流程

    Java 實(shí)戰(zhàn)項(xiàng)目錘煉之網(wǎng)上花店商城的實(shí)現(xiàn)流程

    讀萬(wàn)卷書不如行萬(wàn)里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+jsp+servlet+mysql+ajax實(shí)現(xiàn)一個(gè)網(wǎng)上花店商城系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • Java實(shí)戰(zhàn)之飛翔的小鳥小游戲

    Java實(shí)戰(zhàn)之飛翔的小鳥小游戲

    這篇文章主要介紹了Java實(shí)戰(zhàn)之飛翔的小鳥小游戲,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • 在Java中使用日志框架log4j的方法

    在Java中使用日志框架log4j的方法

    Log4j有三個(gè)主要的組件/對(duì)象:Loggers(記錄器),Appenders (輸出源)和Layouts(布局)。這里可簡(jiǎn)單理解為日志類別,日志要輸出的地方和日志以何種形式輸出,今天通過本文給大家分享Java日志框架log4j的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2021-08-08
  • Java集合操作之List接口及其實(shí)現(xiàn)方法詳解

    Java集合操作之List接口及其實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了Java集合操作之List接口及其實(shí)現(xiàn)方法,詳細(xì)分析了Java集合操作中List接口原理、功能、用法及操作注意事項(xiàng),需要的朋友可以參考下
    2015-07-07
  • Java Spring AOP之PointCut案例詳解

    Java Spring AOP之PointCut案例詳解

    這篇文章主要介紹了Java Spring AOP之PointCut案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • MyBatis使用雪花ID的實(shí)現(xiàn)

    MyBatis使用雪花ID的實(shí)現(xiàn)

    本文主要介紹了MyBatis使用雪花ID的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04

最新評(píng)論