中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發布時間:2022-04-24 13:39:50 來源:億速云 閱讀:204 作者:iii 欄目:開發技術

本篇內容主要講解“Spring Cache怎么使用Redisson分布式鎖解決緩存擊穿問題”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Spring Cache怎么使用Redisson分布式鎖解決緩存擊穿問題”吧!

    1 什么是緩存擊穿

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

    2 為什么要使用分布式鎖

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

    3 什么是Redisson

    Redisson是一個在Redis的基礎上實現的Java駐內存數據網格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對象,還實現了可重入鎖(Reentrant Lock)、公平鎖(Fair Lock、聯鎖(MultiLock)、 紅鎖(RedLock)、 讀寫鎖(ReadWriteLock)等,還提供了許多分布式服務。Redisson提供了使用Redis的最簡單和最便捷的方法。Redisson的宗旨是促進使用者對Redis的關注分離(Separation of Concern),從而讓使用者能夠將精力更集中地放在處理業務邏輯上。

    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服務器連接端口
        host: localhost          # Redis服務器地址
        password: xxxxxx         # Redis服務器連接密碼(默認為空)
        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銷毀時關閉Redisson實例,但不關閉Redis服務
        public RedissonClient redisson() {
            //創建配置
            Config config = new Config();
            /**
             *  連接哨兵:config.useSentinelServers().setMasterName("myMaster").addSentinelAddress()
             *  連接集群: config.useClusterServers().addNodeAddress()
             */
            config.useSingleServer()
                    .setAddress("redis://" + host + ":" + port)
                    .setPassword(password)
                    .setTimeout(5000);
            //根據config創建出RedissonClient實例
            return Redisson.create(config);
        }
    
        @Bean
        public CacheManager RedisCacheManager(RedisConnectionFactory factory) {
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            // 解決查詢緩存轉換異常的問題
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            /**
             * 新版本中om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)已經被廢棄
             * 建議替換為om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL)
             */
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            // 配置序列化解決亂碼的問題
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                    // 設置緩存過期時間  為解決緩存雪崩,所以將過期時間加隨機值
                    .entryTtl(Duration.ofSeconds(60 * 60 + new Random().nextInt(60 * 10)))
                    // 設置key的序列化方式
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                    // 設置value的序列化方式
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
            // .disableCachingNullValues(); //為防止緩存擊穿,所以允許緩存null值
            RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                    .cacheDefaults(config)
                    // 啟用RedisCache以將緩存 put/evict 操作與正在進行的 Spring 管理的事務同步
                    .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
        // 一定要設置sync = true開啟異步,否則會導致多個線程同時獲取到鎖
        @Cacheable(cacheNames = "user", key = "#id", sync = true)
        public User findById(Long id) {
            /**
             *
             * 加了@Cacheable之后方法體執行說明緩存中不存在所查詢的數據
             * 獲取一把鎖,只要鎖的名字一樣,就是同一把鎖
             */
    
            /**
             * 注意: 如果設置了lock.lock(10,TimeUnit.SECONDS) 鎖過期不會自動續期
             *      1、如果我們傳遞了鎖的過期時間,就發送給redis執行腳本,進行占鎖,默認超時就是我們指定的時間
             *      2、如果沒有指定鎖的超時時間,就使用30000L(LockWatchdogTimeout 看門狗的默認時間)
             *      可通過RedissonConfig-->getRedissonClient()-->config.setLockWatchdogTimeout()設置看門狗時間
             *         只要占鎖成功就會啟動一個定時任務【就會重新給鎖設置過期時間,新的時間就是看門狗的默認時間】,每隔10s都會自動續期,續期成30s
             * 看門狗機制
             * 1、鎖的自動續期,如果業務超長,運行期間自動給鎖續上新的30s。不用擔心因為業務時間長,鎖自動過期被刪除
             * 2、加鎖的業務只要運行完成,就不會給當前鎖續期,即使不手動解鎖,鎖默認在30s以后自動刪除
             *
             */
            RLock lock = redissonClient.getLock("redissonClient-lock");
    
            // 對第一個線程執行方法體的線程加鎖,加了@Cacheable,方法執行之后會將方法的返回值存入緩存,下一個線程直接讀取緩存
            lock.lock();
            User user = new User();
            try {
                user = userMapper.selectById(id);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            return user;
        }
    }

    到此,相信大家對“Spring Cache怎么使用Redisson分布式鎖解決緩存擊穿問題”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    阿拉善左旗| 永济市| 鄂伦春自治旗| 云龙县| 武强县| 定陶县| 大新县| 英德市| 琼结县| 呼伦贝尔市| 米泉市| 新疆| 崇仁县| 五指山市| 启东市| 余干县| 庐江县| 磴口县| 墨江| 博白县| 略阳县| 牟定县| 斗六市| 宜川县| 白山市| 东明县| 诸城市| 望谟县| 岳西县| 鲁甸县| 西昌市| 洛宁县| 离岛区| 淳化县| 永宁县| 安义县| 九龙城区| 资讯| 景谷| 隆尧县| 镇平县|