【Redis】基于Redis实现查询缓存

1.缓存更新策略

 主动更新用的最多。
在这里插入图片描述
 主动更新一般是由缓存的调用者,在更新数据库的同时,更新缓存。

操作缓存和数据库时有三个问题需要考虑:

  • 删除缓存还是更新缓存?
    更新缓存:每次更新数据库都更新缓存,无效写操作较多
    删除缓存:更新数据库时让缓存失效,查询时再更新缓存
  • 如何保证缓存与数据库的操作的同时成功或失败?
    单体系统,将缓存与数据库操作放在一个事务
    分布式系统,利用TCC等分布式事务方案
  • 先操作缓存还是先操作数据库?
    先操作数据库,再删除缓存
    先删除缓存,再操作数据库

2.缓存穿透

缓存穿透产生的原因是什么?
 用户请求的数据在缓存中和数据库中都不存在,不断发起这样的请求,给数据库带来巨大压力

缓存穿透的解决方案有哪些?

  • 缓存null值
  • 布隆过滤
  • 增强id的复杂度,避免被猜测id规律
  • 做好数据的基础格式校验
  • 加强用户权限校验
  • 做好热点参数的限流

如下图所示,这里基于缓存空对象实现:
在这里插入图片描述

  • 缓存穿透代码
    // 封装了缓存穿透的处理
    /*
    参数里面需要传递查询数据库的函数
     */
    public <R, ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Long time, TimeUnit unit,
                                          Function<ID, R> dbFallback) {
        // 1.从Redis查询商铺缓存
        String key = keyPrefix + id;
        String json = stringRedisTemplate.opsForValue().get(key);
        // 2.判断是否存在
        if (StrUtil.isNotBlank(json)) {
            // 3.存在则返回缓存数据
            return JSONUtil.toBean(json, type);
        }

        // 命中是否为空的缓存
        if(json != null){
            return null;
        }

        // 4.不存在则查询数据库
        R r = dbFallback.apply(id);
        // 5. 数据库不存在
        if (r == null) {
            // 将空值保存在redis中,应对缓存穿透
            stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
            return null;
        }
        // 6. 数据库存在,写入缓存
        this.set(key, r, time, unit);
        return r;
    }
  • 数据写入缓存代码
    public void set(String key, Object value, Long time, TimeUnit unit) {
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);
    }

3. 缓存雪崩

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
解决方案:

  • 给不同的Key的TTL添加随机值
  • 利用Redis集群提高服务的可用性
  • 给缓存业务添加降级限流策略
  • 给业务添加多级缓存

4.缓存击穿

 缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
 常见的解决方案有两种:

  • 互斥锁
  • 逻辑过期
    在这里插入图片描述

互斥锁

在这里插入图片描述

    // 互斥锁解决缓存击穿
    public <R,ID> R queryWithMutex(String keyPrefix, ID id, Class<R> type, Long time, TimeUnit unit, Function<ID, R> dbFallback) {
        // 1.从Redis查询商铺缓存
        String key = keyPrefix + id;
        String json = stringRedisTemplate.opsForValue().get(key);
        // 2.判断是否存在
        if (StrUtil.isNotBlank(json)) {
            // 3.存在则返回缓存数据

            return JSONUtil.toBean(json, type);
        }

        // 命中是否为空的缓存
        if(json != null){
            return null;
        }

        // 4. 实现缓存重建
        // 4.1 尝试获取分布式锁
        String lockKey = "lock:shop:" + id;
        R r = null;
        try {
            boolean isLock = tryLock(lockKey);

            // 4.2 判断是否获取成功
            if (!isLock) {
                // 4.3 失败,则失眠并重试
                Thread.sleep(50);
                return queryWithMutex(keyPrefix, id, type, time, unit, dbFallback);
            }


            // 4.4 成功则查询数据库

            r = dbFallback.apply(id);
            // 5. 数据库不存在
            if (r == null) {
                // 将空值保存在redis中,应对缓存穿透
                this.set(key, "", time, unit);
                return null;
            }
            // 6. 数据库存在,写入缓存
            this.set(key, r, time, unit);

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 7. 释放锁
            releaseLock(lockKey);
        }

        return r;
    }
  • 锁相关代码
   // 线程池
    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

    // 尝试获取分布式锁
    private boolean tryLock(String key) {
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        // 这样更安全
        return BooleanUtil.isTrue(lock);
    }

    // 释放分布式锁
    private void releaseLock(String key) {
        stringRedisTemplate.delete(key);
    }

逻辑过期

在这里插入图片描述

  • 逻辑过期存入数据
    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {
        RedisData redisData = new RedisData();
        redisData.setData(value);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));

        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
    }
  • 逻辑过期锁查询数据
    public <R,ID> R queryWithLogicalExpire(String keyPrefix ,ID id, Class<R> type, Long time, TimeUnit unit, Function<ID, R> dbFallback) {
        // 1.从Redis查询商铺缓存
        String key = keyPrefix + id;
        String json = stringRedisTemplate.opsForValue().get(key);
        // 2.判断是否存在
        if (StrUtil.isBlank(json)) {
            // 3.不存在
            return null;
        }
        // 4. 命中,json反序列化对象
        RedisData redisData = JSONUtil.toBean(json, RedisData.class);
        JSONObject data = (JSONObject) redisData.getData();
        R r = JSONUtil.toBean(data, type);
        LocalDateTime expireTime = redisData.getExpireTime();
        // 5. 判断是否过期
        if (expireTime.isAfter(LocalDateTime.now())) {
            // 5.1 未过期直接返回
            return r;
        }
        // 5.2 过期则异步更新缓存

        // 6. 缓存重建
        // 6.1 尝试获取分布式锁
        String lockKey = LOCK_SHOP_KEY + id;
        // 6.2 判断是否获取成功
        if (tryLock(lockKey)) {
            // 6.3 成功,开启独立线程实现缓存重建
            CACHE_REBUILD_EXECUTOR.submit(()->{
                // 重建缓存
                try {
                    // 查数据库
                    R r1 = dbFallback.apply(id);
                    // 保存在redis中
                    this.setWithLogicalExpire(key, r1, time, unit);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    releaseLock(lockKey);
                }

            });
        }
        // 6.4 返回过期的信息
        return r;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/274006.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IDEA集成Github

1.分析工程到 Gitee 新建一个项目初始化本地库添加到暂存区添加到本地库 在 Gitee 上可以查看到刚才提交的内容 2 .将本地代码 push 到远程库 新建一个码云仓库新建一个项目初始化本地库添加到暂存区添加到本地库 到码云仓库查看 3.pull 拉取远程库到本地库 在码云…

C语言 扫雷游戏

写了这么长时间的关于C语言的基础知识&#xff0c;相信大家已经学会了使用C语言书写一些基础的代码&#xff0c;上次还编写了三子棋游戏的代码&#xff0c;这次我将编写一个基础版的扫雷游戏。 首先&#xff0c;创建三个文件&#xff0c;两个源文件&#xff0c;一个头文件&…

Python之Web开发中级教程----Django站点管理

Python之Web开发中级教程----Django站点管理 网站的开发分为两部分&#xff1a;内容发布和公共访问 内容发布是由网站的管理员负责查看、添加、修改、删除数据 Django能够根据定义的模型类自动地生成管理模块 使用Django的管理模块, 需要按照如下步骤操作 : 1.管理界面本地…

【C语言】C语言内存函数

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《C语言》 &#x1f389;道阻且长&#xff0c;行则将至 前言 这篇博客是关于C语言内存函数(memcpy,memmove,memset,memcmp)的使用以及部分的模拟实现 memcpy,memmove,memset,memc…

防火墙的原理和配置

“防火墙”一词起源于建筑领域&#xff0c;用来隔离火灾&#xff0c;阻止火势从一个区域蔓延到另一个区域。引入到通信领域&#xff0c;防火墙这一具体设备通常用于两个网络之间有针对性的、逻辑意义上的隔离。这种隔离是选择性的&#xff0c;隔离“火”的蔓延&#xff0c;而又…

《计算机视觉中的深度学习》之目标检测算法原理

参考&#xff1a;《计算机视觉中的深度学习》 概述 目标检测的挑战&#xff1a; 减少目标定位的准确度减少背景干扰提高目标定位的准确度 目标检测系统常用评价指标&#xff1a;检测速度和精度 提高精度&#xff1a;有效排除背景&#xff0c;光照和噪声的影响 提高检测速度…

JAVA八股day1

遇到的问题 相比于包装类型&#xff08;对象类型&#xff09;&#xff0c; 基本数据类型占用的空间往往非常小为什么说是几乎所有对象实例都存在于堆中呢&#xff1f;静态变量和成员变量、成员变量和局部变量的区别为什么浮点数运算的时候会有精度丢失的风险&#xff1f;如何解…

Re62:读论文 GPT-2 Language Models are Unsupervised Multitask Learners

诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文全名&#xff1a;Language Models are Unsupervised Multitask Learners 论文下载地址&#xff1a;https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learner…

手机备忘录怎么导出到电脑,如何将手机备忘录导出到电脑

备忘录是我们日常生活和工作中常用的工具之一&#xff0c;我们可以在手机上轻松地记录重要的事务、想法和灵感。然而&#xff0c;在某些情况下&#xff0c;我们可能需要将手机备忘录导出到电脑进行更详细的整理和管理。那么&#xff0c;手机备忘录怎么导出到电脑&#xff0c;如…

性能测试-Jmeter常用元件基础使用

一、Jmeter元件 #线程组 添加HTTP请求 #配置元件 配置元件内的元件都是用于进行初始化的东西 #监听器 监听器主要是用来获取我们使用取样器发送请求后的响应数据相关信息 #定时器 定时器主要用来控制我们多久后执行该取样器&#xff08;发送请求&#xff09; #前置处理器 前置处…

【每日一问】手机如何开启USB调试?

一、背景 当电脑跟手机之间需要进行交互的时候&#xff0c;可以考虑使用usb进行连接。那么手机如何开启USB调试呢&#xff1f; 二、操作步骤&#xff1a; 思路&#xff1a; 步骤1&#xff1a;手机开启开发者模式 步骤2&#xff1a;在开发者模式中&#xff0c;开启“USB调试”…

elment-ui el-tabs组件 每次点击后 created方法都会执行2次

先看错误的 日志打印: 错误的代码如下: 正确的日志打印: 正确的代码如下: 前言: 在element-ui的tabs组件中,我们发现每次切换页面,所有的子组件都会重新渲染一次。当子页面需要发送数据请求并且子页面过多时,这样会过多的占用网络资源。这里我们可以使用 v-if 来进行…

【四 (4)数据可视化之 Ploty Express常用图表及代码实现 】

目录 文章导航一、介绍二、安装Plotly Express三、导入Plotly Express四、占比类图表1、饼图2、环形图3、堆叠条形图4、百分比堆叠条形图 五、比较排序类1、条形图2、漏斗图3、面积漏斗图 六、趋势类图表1、折线图2、多图例折线图3、分列折线图4、面积图5、多图例面积图 七、频…

layuiAdmin-通用型后台模板框架【广泛用于各类管理平台】

1. 主页 1.1 控制台 2. 组件 3. 页面 3.1 个人主页 3.2 通讯录 3.3 客户列表 3.4 商品列表 3.5 留言板 3.6 搜索结果 3.7 注册 3.8 登入 3.9 忘记密码 4. 应用 4.1 内容系统 4.1.1 文章列表 4.1.2 分类管理 4.1.3 评论管理 4.2 社区系统 4.2.1 帖子列表 4.2.2 回…

html5播放flv视频

参考&#xff1a;flv-h265 - npmHTML5 FLV Player. Latest version: 1.7.0, last published: 6 months ago. Start using flv-h265 in your project by running npm i flv-h265. There are no other projects in the npm registry using flv-h265.https://www.npmjs.com/packag…

网页429:请求过多

网页429&#xff1a;请求过多 当您的计算机或设备向网站发送太多请求时&#xff0c;尤其是在尝试登录或访问网站的高安全性部分时&#xff0c;可能会收到这个错误。这通常是因为服务器设置了速率限制器&#xff0c;以防止恶意攻击或过度使用。 尝试以下方法 等待一段时间后…

ET框架新起一个服务及实现服务之间的消息通讯

ET框架是熊猫大大写的双端框架&#xff0c;游戏客户端和服务端都是用C#语言来编写 新起一个服务名比如叫做Activity 1.配置文件StartSceneConfig 2. SceneFactory switch (scene.SceneType) {case SceneType.Activity:break; } 定义SceneType枚举类型 public enum SceneTyp…

java毕业设计 | springboot+vue游戏交流网站(附源码)

1&#xff0c; 概述 本次的毕业设计主要就是在基于Java语言平台下设计并开发一个游戏网站系统软件。运用当前Google提供的Java来实现对游戏信息和游戏道具查询。当然使用的数据库是Mysql。尽管没有面向对象的数据库的作用强大&#xff0c;但是在Java开发上还是比较的灵活和方便…

中国传统游戏-幻方-c/c++实现

幻方&#xff08;Magic Square&#xff09;是一种将数字安排在正方形格子中&#xff0c;使每行、列和对角线上的数字和都相等的方法。 幻方也是一种中国传统游戏。旧时在官府、学堂多见。它是将从一到若干个数的自然数排成纵横各为若干个数的正方形&#xff0c;使在同一行、同…

html--蝴蝶

<!DOCTYPE html> <html lang"en" > <head> <meta charset"UTF-8"> <title>蝴蝶飞舞</title> <link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.cs…
最新文章