IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Redis缓存穿透缓存击穿缓存雪崩解决方案以及封装Redis工具类 -> 正文阅读

[大数据]Redis缓存穿透缓存击穿缓存雪崩解决方案以及封装Redis工具类

参考自黑马程序员

缓存穿透

缓存穿透: 数据在数据库和redis中都不存在,请求直接打在数据库中。
在这里插入图片描述
解决方案:

方案一:
在这里插入图片描述
在这里插入图片描述
方案二:
在这里插入图片描述
在这里插入图片描述
代码案例实现:
在这里插入图片描述

 	// 缓存穿透
    private Shop cacheBreakDown(Long id){
        // 查询redis
        String jsonShop = (String) redisTemplate.opsForValue().get(RedisConstants.SHOP_CACHE + id);
        // 既不是""也不是null,命中直接返回
        if (StrUtil.isNotBlank(jsonShop)) {
            return JSONUtil.toBean(jsonShop, Shop.class);
        }
        // 判断是否命中空值"",命中直接返回
        if (jsonShop != null) {
            return null;
        }
        // 查询数据库
        Shop shop = this.getById(id);
        if (shop == null) {
            // 将空值""写入redis,防止缓存穿透
            redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE + id, "", RedisConstants.SHOP_CACHE_TTL, TimeUnit.SECONDS);
            return null;
        }
        jsonShop=JSONUtil.toJsonStr(shop);
        redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE + id, jsonShop, RedisConstants.SHOP_CACHE_TTL, TimeUnit.MINUTES);
        return shop;
    }

缓存击穿

在这里插入图片描述
互斥锁时序图
在这里插入图片描述
代码实现:

	// 加锁
    private boolean tryLock(String key){
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, "1", Duration.ofMillis(10000));
        return BooleanUtil.isTrue(ifAbsent);
    }
    // 解锁
    private void unLock(String key){
        redisTemplate.delete(key);
    }

在这里插入图片描述

	 // 缓存击穿
    private Shop cachePenetrate(long id){
        // 查询redis
        String jsonShop = (String) redisTemplate.opsForValue().get(RedisConstants.SHOP_CACHE + id);
        if (StrUtil.isNotBlank(jsonShop)) {
            // 直接返回
            return JSONUtil.toBean(jsonShop, Shop.class);
        }
        // 判断是否命中空值 ""
        if (jsonShop != null) {
            return null;
        }
        Shop shop = null;
        try {
            // 实现缓存重建
            if (!tryLock(RedisConstants.LOCK_KEY+id)){
                // 未获得锁,休眠重试
                Thread.sleep(100);
                return cachePenetrate(id);
            }
            // 查询数据库
            shop = this.getById(id);
            if (shop == null) {
                // 将空值写入redis,防止缓存穿透
                redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE + id, "", RedisConstants.SHOP_CACHE_TTL, TimeUnit.SECONDS);
                return null;
            }
            jsonShop=JSONUtil.toJsonStr(shop);
            redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE + id, jsonShop, RedisConstants.SHOP_CACHE_TTL, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            throw new RuntimeException("被打断");
        } finally {
            // 释放锁
            unLock(RedisConstants.LOCK_KEY+id);
        }
        return shop;
    }

逻辑过期时序图
在这里插入图片描述
代码实现:
在这里插入图片描述

@Data
public class RedisData {
	// 逻辑过期时间
    private LocalDateTime expireTime;
    // 要存入redis的数据
    private Object data;
}
// 重建的线程池
private ThreadPoolExecutor cacheThreadPool=new ThreadPoolExecutor(
            5,10,1,TimeUnit.MINUTES,new ArrayBlockingQueue<>(10));
            
 private Shop cacheLogicExpire(long id){
        // 查询redis
        String jsonShop = (String) redisTemplate.opsForValue().get(RedisConstants.SHOP_CACHE + id);
        // 没有命中直接返回null
        if (StrUtil.isBlank(jsonShop)) {
            return null;
        }
        // 反序列化
        RedisData redisData= JSONUtil.toBean(jsonShop, RedisData.class);
        JSONObject jsonObject= (JSONObject) redisData.getData();
        Shop shop = JSONUtil.toBean(jsonObject, Shop.class);
        //  命中了判断过期时间是否过期,时间没有过期直接返回数据
        if (redisData.getExpireTime().isAfter(LocalDateTime.now())) {
            return shop;
        }
        // 过期了尝试获得锁
        if (tryLock(RedisConstants.LOCK_KEY+id)) {
            // 开启线程缓存重建
            cacheThreadPool.execute(()->{
                try {
                    saveData2Redis(id,1800);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    // 释放锁
                    unLock(RedisConstants.LOCK_KEY+id);
                }
            });
        }
        // 返回过期数据
        return shop;
    }

    public void saveData2Redis(long id,long time){
        Shop shop = getById(id);
        RedisData redisData = new RedisData();
        redisData.setData(shop);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(time));
        redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE,JSONUtil.toJsonStr(redisData));
    }

缓存雪崩

在这里插入图片描述

封装工具类

在这里插入图片描述
方法1:

    /**
     *
     * @param key key
     * @param value value
     * @param expireTime key过期时间
     * @param unit 时间单位
     */
    public void set(String key, Object value, long expireTime, TimeUnit unit){
        redisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(value),expireTime,unit);
    }

方法2:

  /**
     *
     * @param key key
     * @param value value
     * @param expireTime key过期时间
     * @param unit 时间单位
     */
    public void setWithLogicalTime(String key, Object value, long expireTime, TimeUnit unit){
        RedisData redisData = new RedisData();
        long seconds = unit.toSeconds(expireTime);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(seconds));
        redisData.setData(value);
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
    }

方法3:

 /**
     *
     * @param keyPrefix 要查询数据的key前缀
     * @param id 要查询的数据id
     * @param type 数据类型
     * @param dbOpe 查询数据方法
     * @param expireTime 过期时间
     * @param unit 时间单位
     * @param <R> 返回的数据类型
     * @param <ID> 要查询数据的ID类型
     * @return 要查询的数据
     */
    public <R,ID> R getCache(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbOpe,long expireTime,TimeUnit unit){
        String key=keyPrefix+id;
        // 先查询Redis
        String value = (String) redisTemplate.opsForValue().get(key);
        // 不为空且不为""直接返回
        if (StrUtil.isNotBlank(value)){
            return JSONUtil.toBean(value,type);
        }
        // 如果为""不为null返回null
        if (value!=null){
            return null;
        }
        // 根据id查询数据库
        R result = dbOpe.apply(id);
        // 数据库也为空
        if (result == null) {
            // 缓存""防止缓存穿透
            redisTemplate.opsForValue().set(key,"",expireTime,unit);
            return null;
        }
        // 不为空缓存重建
        redisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(result),expireTime,unit);
        return result;
    }

方法4:

 /**
     *
     * @param keyPrefix 要查询数据的key前缀
     * @param id 要查询的数据id
     * @param type 数据类型
     * @param dbOpe 查询数据方法
     * @param expireTime 过期时间
     * @param unit 时间单位
     * @param <R> 返回的数据类型
     * @param <ID> 要查询数据的ID类型
     * @return 要查询的数据
     */
    public <R,ID> R getCacheWithLogicExpire(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbOpe, long expireTime, TimeUnit unit){
        String key=keyPrefix+id;
        // 查询redis
        String value = (String) redisTemplate.opsForValue().get(key);
        // 为空返回null
        if (StrUtil.isBlank(value)){
            return null;
        }
        // 反序列化为RedisData
        RedisData redisData = JSONUtil.toBean(value, RedisData.class);
        R result = JSONUtil.toBean((JSONObject) redisData.getData(), type);
        // 判断是否过期,没有过期直接返回
        if (LocalDateTime.now().isAfter(redisData.getExpireTime())){
            return result;
        }
        // 过期了尝试获得锁
        if (tryLock(key)){
            // 开启线程缓存重建
            cacheThreadPool.execute(()->{
                try {
                    R r = dbOpe.apply(id);
                    RedisData redis_data = new RedisData();
                    redis_data.setData(r);
                    redis_data.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(expireTime)));
                    redisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redis_data));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    // 释放锁
                    unLock(RedisConstants.LOCK_KEY+id);
                }
            });
        }
        return result;
    }
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 13:08:12  更:2022-03-06 13:09:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:47:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码