Redis实战:缓存穿透及其解决思路 实战演示

🎉🎉欢迎光临,终于等到你啦🎉🎉

🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀

🌟持续更新的专栏Redis实战与进阶

本专栏讲解Redis从原理到实践

这是苏泽的个人主页可以看到我其他的内容哦👇👇

努力的苏泽icon-default.png?t=N7T8http://suzee.blog.csdn.net


缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,导致每次请求都要访问数据库,增加数据库的负载。为了解决缓存穿透问题

目录

缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,导致每次请求都要访问数据库,增加数据库的负载。为了解决缓存穿透问题

大致以下几种方案:

缓存空对象

解决思路:

实现代码: 

布隆过滤器(Bloom Filter):

增加ID复杂度:在缓存层之前,可以对传入的ID进行一定的复杂处理,比如加密、哈希等,使得恶意请求的ID难以构造,从而减少缓存穿透的可能性。

做好数据格式校验:在应用层面对传入的数据进行格式校验,只有符合预定格式的数据才能继续进行缓存查询操作,否则直接返回错误响应,避免无效的数据库查询操作。

加强用户权限校验:在缓存查询之前,进行用户权限校验,确保用户具有合法的访问权限,避免未授权的访问触发数据库查询操作。


大致以下几种方案:

  1. 缓存null

  2. 布隆过滤

  3. 增加id复杂度

  4. 做好数据格式校验

  5. 加强用户权限校验

  6. 做好热点参数的限流

缓存空对象

解决思路:

1,如果查询也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。
 

2,根据缓存数据Key的规则。例如我们公司是做机顶盒的,缓存数据以Mac为Key,Mac是有规则,如果不符合规则就过滤掉,这样可以过滤一部分查询。在做缓存规划的时候,Key有一定规则的话,可以采取这种办法。这种办法只能缓解一部分的压力,过滤和系统无关的查询,但是无法根治。
 

3,采用布隆,将所有可能存在的数据哈希到一个足够大的BitSet中,不存在的数据将会被拦截掉,从而避免了对存储系统的查询压力。关于布隆,详情查看:基于BitSet的布隆过滤器(Bloom Filter) 

实现代码: 

@Override
public Result queryById(Long id) {
    //1.从Redis查询id  这里使用的数据结构可以是String也可以是hash  若是查询不到就为空了 CACHE_SHOP_KEY就是"cache:shop:"
    String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
    //2.判断是否存在
    if (StrUtil.isNotBlank(shopJson)) {
        //3.存在直接返回
        Shop shop = JSONUtil.toBean(shopJson, Shop.class);
        return  Result.ok(shop);
    }
    //4.不存在 查询数据库
    Shop shop = getById(id);
    //5.数据库中不存在 返回报错
    if (shop == null){
        //空值写入redis
        stringRedisTemplate.opsForValue().set("cache:shop:" + id, null,CACHE_NULL_TTL, TimeUnit.MINUTES);
        return Result.fail("404");
    }
    //判断是否命中null 命中则拦截  shopJson为缓存中的商品信息
    if (shopJson == null){
        return  Result.fail("404");
    }
    //6.数据库中存在  写入Redis  并返回
    stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);

    return Result.ok(shop);
}

布隆过滤器(Bloom Filter):


布隆过滤器是一种空间效率高、误判率低的数据结构,可以用于快速判断一个元素是否存在于一个集合中。在解决缓存穿透问题时,可以使用布隆过滤器在查询缓存之前进行快速判断,如果判断不存在,则可以直接返回,而不触发后续的数据库查询操作。

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

public class BloomFilterExample {
    public static void main(String[] args) {
        int expectedInsertions = 1000000;
        double falsePositiveRate = 0.01;

        BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.unencodedCharsFunnel(), expectedInsertions, falsePositiveRate);

        // 添加数据到布隆过滤器
        bloomFilter.put("data1");
        bloomFilter.put("data2");

        // 查询数据是否存在于布隆过滤器中
        System.out.println(bloomFilter.mightContain("data1"));  // 输出:true
        System.out.println(bloomFilter.mightContain("data3"));  // 输出:false
    }
}

增加ID复杂度:
在缓存层之前,可以对传入的ID进行一定的复杂处理,比如加密、哈希等,使得恶意请求的ID难以构造,从而减少缓存穿透的可能性。

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class IdComplexityExample {
    public static void main(String[] args) {
        String id = "data_id";

        // 对ID进行哈希处理
        String processedId = hashId(id);
        System.out.println(processedId);
    }

    public static String hashId(String id) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] hashedBytes = md.digest(id.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : hashedBytes) {
                sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}

做好数据格式校验:
在应用层面对传入的数据进行格式校验,只有符合预定格式的数据才能继续进行缓存查询操作,否则直接返回错误响应,避免无效的数据库查询操作。

public class DataFormatValidationExample {
    public static void main(String[] args) {
        String data = "data";

        // 校验数据格式
        if (validateData(data)) {
            System.out.println("Data format is valid");
        } else {
            System.out.println("Invalid data format");
        }
    }

    public static boolean validateData(String data) {
        // 校验数据格式
        return data instanceof String;
    }
}

加强用户权限校验:
在缓存查询之前,进行用户权限校验,确保用户具有合法的访问权限,避免未授权的访问触发数据库查询操作。

public class UserPermissionValidationExample {
    public static void main(String[] args) {
        String userId = "user123";
        String dataId = "data123";

        // 验证用户权限
        if (validateUserPermission(userId)) {
            // 查询缓存
            String result = queryCache(dataId);
            if (result != null) {
                System.out.println("Cache hit: " + result);
            } else {
                System.out.println("Cache miss");
                // 查询数据库
                result = queryDatabase(dataId);
                // 将结果放入缓存
                putCache(dataId, result);
                System.out.println("Result: " + result);
            }
        } else {
            System.out.println("User does not have permission to access the data");
        }
    }

    public static boolean validateUserPermission(String userId) {
        //对不起,由于GPT-3.5语言模型的限制,我无法为您提供完整的Java代码示例。我只能提供一些伪代码来说明解决方案的思路。请根据以下伪代码示例进行实际的Java代码实现。

4. 加强用户权限校验:
在缓存查询之前,进行用户权限校验,确保用户具有合法的访问权限,避免未授权的访问触发数据库查询操作。

示例伪代码:
```java
public class UserPermissionValidationExample {
    public static void main(String[] args) {
        String userId = "user123";
        String dataId = "data123";

        // 验证用户权限
        if (validateUserPermission(userId)) {
            // 查询缓存
            String result = queryCache(dataId);
            if (result != null) {
                System.out.println("Cache hit: " + result);
            } else {
                System.out.println("Cache miss");
                // 查询数据库
                result = queryDatabase(dataId);
                // 将结果放入缓存
                putCache(dataId, result);
                System.out.println("Result: " + result);
            }
        } else {
            System.out.println("User does not have permission to access the data");
        }
    }

    public static boolean validateUserPermission(String userId) {
        // 根据用户ID进行权限验证,返回验证结果
        // 示例:从数据库或用户管理系统中查询用户权限信息,并验证用户是否具有访问权限
        // 返回 true 表示有权限,返回 false 表示无权限
        // 实际实现需要根据具体的业务逻辑进行判断和验证
        // 例如:
        // if (userHasPermission(userId)) {
        //     return true;
        // } else {
        //     return false;
        // }
    }

    public static String queryCache(String dataId) {
        // 查询缓存,返回缓存中的数据
        // 示例:使用缓存客户端库查询缓存,并返回查询结果
        // 实际实现需要根据具体的缓存系统和客户端库进行调用和处理
        // 例如:
        // return cacheClient.get(dataId);
    }

    public static String queryDatabase(String dataId) {
        // 查询数据库,返回数据库中的数据
        // 示例:使用数据库客户端库查询数据库,并返回查询结果
        // 实际实现需要根据具体的数据库系统和客户端库进行调用和处理
        // 例如:
        // return dbClient.query("SELECT * FROM data_table WHERE id = ?", dataId);
    }

    public static void putCache(String dataId, String data) {
        // 将数据放入缓存
        // 示例:使用缓存客户端库将数据存入缓存
        // 实际实现需要根据具体的缓存系统和客户端库进行调用和处理
        // 例如:
        // cacheClient.put(dataId, data);
    }
}

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

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

相关文章

YOLOv5改进 | 图像去雾 | 利用图像去雾网络AOD-PONO-Net网络增改进图像物体检测(全网独家首发)

一、本文介绍 本文给大家带来的改进机制是利用AODNet图像去雾网络结合PONO机制实现二次增强&#xff0c;我将该网络结合YOLOv5针对图像进行去雾检测&#xff08;也适用于一些模糊场景&#xff0c;图片不清晰的检测&#xff09;&#xff0c;同时本文的内容不影响其它的模块改进…

【嵌入式硬件】步进电机

1.步进电机简介 1.1步进电机基本原理 步进电机的英文是stepping motor。step的中文意思是行走、迈步。所以仅从字面上我们就可以得知,步进电机就是一步一步移动的电动机。说的官方一点儿,步进电机是一种将电脉冲信号转换成相应角位移或者线位移的电动机(直线电机)。下图为…

交通事故档案管理系统|基于JSP技术+ Mysql+Java+Tomcat的交通事故档案管理系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

AI程序员的崛起还是人类程序员的消亡?

最近有个对互联网行业影响极大的爆炸新闻&#xff1a; 我们这个圈子里有了个新玩家——AI程序员。 没错&#xff0c;就是那种用人工智能技术驱动的编程助手。你可能听说过Devin&#xff0c;这家伙不是一般的程序员&#xff0c;它能迅速学习新技术&#xff0c;还能在编码、找bug…

【.net/.net core】后台生成echarts图片解决方案及.net core html转word方法

工具环境下载&#xff1a; EChartsConvert&#xff1a;https://gitee.com/saintlee/echartsconvert EChartsConvert为生成echarts图片的服务端&#xff0c;用于接收参数和生成echarts图表图片BASE64编码 PhantomJS:Download PhantomJS PhantomJS用来发布EChartsConvert服务…

媒资管理模块之文件预览

文件预览 图片和视频上传成功后&#xff0c;可以通过预览按钮查看文件内容(如图片和MP4格式视频),对于浏览器不支持查看的文件提示错误信息(如avi格式视频) 接口定义 根据上传文件的ID获取文件对应的可访问URL,对于图片或MP4格式的视频可通过浏览器直接预览,对于其他文件如.a…

WEB 表单练习题

任务如图&#xff1a; <html><head><meta charest"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head><body><table width"…

Flink GateWay、HiveServer2 和 hive on spark

Flink SQL Gateway简介 从官网的资料可以知道Flink SQL Gateway是一个服务&#xff0c;这个服务支持多个客户端并发的从远程提交任务。Flink SQL Gateway使任务的提交、元数据的查询、在线数据分析变得更简单。 Flink SQL Gateway的架构如下图&#xff0c;它由插件化的Endpoi…

将本地的项目上传到gitee,

场景&#xff1a;在本地有一个项目&#xff0c;想要把这个项目上传到gitee&#xff0c;且在gitee中已经创建好仓库 依次执行下图中的命令&#xff1a;

WebXR实践——利用aframe框架浏览器展示全景图片

一、效果 话不多说&#xff0c;先上效果 二、代码 index.html <!DOCTYPE html> <html><head><meta charset"utf-8"><title>360&deg; Image</title><meta name"description" content"360&deg; Imag…

Head First Design Patterns -适配器模式与外观模式

适配器模式 什么是适配器模式 适配器模式&#xff0c;将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作。 类图 代码 利用Enumeration来适配Iterator&#xff0c;外部只需要调用这个适配器&#xff0c;即可以像调用Iterator那样&#xff0c;…

[音视频学习笔记]六、自制音视频播放器Part1 -新版本ffmpeg,Qt +VS2022,都什么年代了还在写传统播放器?

前言 参考了雷神的自制播放器项目&#xff0c;100行代码实现最简单的基于FFMPEGSDL的视频播放器&#xff08;SDL1.x&#xff09; 不过老版本的代码参考意义不大了&#xff0c;我现在准备使用Qt VS2022 FFmpeg59重写这部分代码&#xff0c;具体的代码仓库如下&#xff1a; …

市场复盘总结 20240321

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 23% 最常用的…

基于python+vue银行柜台管理系统flask-django-php-nodejs

课题主要采用python开发语言、django框架和MySQL数据库开发技术以及基于Eclipse的编辑器。系统主要包括通知信息、用户信息、银行信息、卡号账户、存款信息管理、取款信息、转账信息、贷款信息、贷款申请、贷款发放、账单信息、还款信息等功能&#xff0c;从而实现智能化的管理…

中文编程入门(Lua5.4.6中文版)第十一章 Lua 模块与包 参考星争际霸游戏

在遥远的星争际霸世界中&#xff0c;代码模块就如同星际基地中的高科技仓库&#xff0c;储存着各类经过封装优化的战术指令和战略资源。自Lua 5.1版本起&#xff0c;星际编程者们引入了标准化的模块管理系统&#xff0c;使得不同战舰之间能够共享和调用核心战斗算法&#xff0c…

python智慧农业小程序flask-django-php-nodejs

当今社会已经步入了科学技术进步和经济社会快速发展的新时期&#xff0c;国际信息和学术交流也不断加强&#xff0c;计算机技术对经济社会发展和人民生活改善的影响也日益突出&#xff0c;人类的生存和思考方式也产生了变化。传统智慧农业采取了人工的管理方法&#xff0c;但这…

Golang Gorm 自动分批查询

场景&#xff1a; 目标查询全量数据&#xff0c;但需要每次Limit分批查询&#xff0c;保护数据库 文档&#xff1a; https://gorm.io/zh_CN/docs/advanced_query.html // Param: // dest 目标地址 // batchSize 大小 // fc 处理函数func (db *DB) FindInBatc…

sentinel熔断规则详解

1、慢调用降级熔断 1.1、参数详解 最大RT&#xff1a;调用接口的最大时间。 比例阈值&#xff1a;超过了最大RT调用时间的请求的比例。 熔断时长&#xff1a;触发熔断后&#xff0c;熔断的时间 最小请求数据&#xff1a;每秒最少的请求数量&#xff0c;只有大于等于这个数…

微信小程序button动态跳转到页面

微信小程序中如何动态的跳转到某个页面。 目录 1、首先在js文件中定义事件函数 2、在页面中进行传参调用 3、其它跳转方法简单说明 1、首先在js文件中定义事件函数 goto(e){const urle.currentTarget.dataset.url;wx.navigateTo({url: url})}, 2、在页面中进行传参调用 &l…

在 3D 虚拟城市中展示自定义建筑

在本教程中&#xff0c;您将学习如何创建 Cesium 应用程序&#xff0c;用您自己的 3D 模型替换真实城市中的建筑物。您可以使用它来可视化拟建建筑的影响&#xff0c;及如何改变天际线&#xff1f;从特定楼层或房间看到的景色会是什么样子&#xff1f; 我们将介绍如何&#xf…