Redis(80)如何解决Redis的缓存穿透问题?

作者:Victor356日期:2025/10/24

解决Redis缓存穿透问题的方法有多种,具体的解决方案可以根据实际情况选择。以下是几种常见的解决方法及详细的代码示例,包括缓存空结果、使用布隆过滤器以及参数校验。

1. 缓存空结果

当查询数据库返回空结果时,也将其缓存起来,并设置一个较短的过期时间,比如5分钟。这样即使请求数据不存在,也不需要每次都访问数据库。

示例代码:

1import redis.clients.jedis.Jedis;
2import com.fasterxml.jackson.databind.ObjectMapper;
3
4public class CachePenetrationExample {
5    private Jedis jedis;
6    private static final ObjectMapper objectMapper = new ObjectMapper();
7
8    public CachePenetrationExample(Jedis jedis) {
9        this.jedis = jedis;
10    }
11
12    public <T> T getCachedData(String key, Class<T> clazz, DataProvider<T> provider, int cacheTime) {
13        try {
14            String cacheValue = jedis.get(key);
15            if (cacheValue != null) {
16                if (cacheValue.equals("null")) {
17                    return null;
18                }
19                return objectMapper.readValue(cacheValue, clazz);
20            }
21
22            T data = provider.getData();
23            if (data == null) {
24                jedis.setex(key, 300, "null"); // 缓存空结果5分钟
25            } else {
26                jedis.setex(key, cacheTime, objectMapper.writeValueAsString(data));
27            }
28            return data;
29        } catch (Exception e) {
30            e.printStackTrace();
31            return null;
32        }
33    }
34
35    public interface DataProvider<T> {
36        T getData();
37    }
38
39    public static void main(String[] args) {
40        Jedis jedis = new Jedis("localhost", 6379);
41        CachePenetrationExample cache = new CachePenetrationExample(jedis);
42
43        String userId = "nonexistent";
44        String cacheKey = "user:" + userId;
45        int cacheTime = 3600; // 缓存 1 小时
46
47        User user = cache.getCachedData(cacheKey, User.class, () -> {
48            // 模拟数据库查询,返回null表示数据不存在
49            return null;
50        }, cacheTime);
51
52        if (user == null) {
53            System.out.println("User not found.");
54        } else {
55            System.out.println("User: " + user);
56        }
57
58        jedis.close();
59    }
60
61    static class User {
62        private String id;
63        private String name;
64        private String email;
65
66        // Getters and Setters
67
68        @Override
69        public String toString() {
70            return "User{id='" + id + "', name='" + name + "', email='" + email + "'}";
71        }
72    }
73}
74

2. 布隆过滤器

使用布隆过滤器快速判断数据是否存在。布隆过滤器是一种概率型数据结构,它能以较低的内存消耗判断一个元素是否存在某个集合中。虽然存在误判的可能,但不会漏判。

示例代码:

1import com.google.common.hash.BloomFilter;
2import com.google.common.hash.Funnels;
3import redis.clients.jedis.Jedis;
4import java.nio.charset.Charset;
5
6public class BloomFilterExample {
7    private Jedis jedis;
8    private BloomFilter<String> bloomFilter;
9
10    public BloomFilterExample(Jedis jedis, int expectedInsertions, double falsePositiveProbability) {
11        this.jedis = jedis;
12        this.bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), expectedInsertions, falsePositiveProbability);
13        // 初始化布隆过滤器,通常从数据库加载已有数据
14        initializeBloomFilter();
15    }
16
17    private void initializeBloomFilter() {
18        // 模拟加载数据
19        bloomFilter.put("existingUser1");
20        bloomFilter.put("existingUser2");
21        // 真实环境中应从数据库加载
22    }
23
24    public <T> T getCachedData(String key, Class<T> clazz, DataProvider<T> provider, int cacheTime) {
25        try {
26            if (!bloomFilter.mightContain(key)) {
27                return null;
28            }
29
30            String cacheValue = jedis.get(key);
31            if (cacheValue != null) {
32                if (cacheValue.equals("null")) {
33                    return null;
34                }
35                return objectMapper.readValue(cacheValue, clazz);
36            }
37
38            T data = provider.getData();
39            if (data == null) {
40                jedis.setex(key, 300, "null"); // 缓存空结果5分钟
41            } else {
42                jedis.setex(key, cacheTime, objectMapper.writeValueAsString(data));
43            }
44            return data;
45        } catch (Exception e) {
46            e.printStackTrace();
47            return null;
48        }
49    }
50
51    public interface DataProvider<T> {
52        T getData();
53    }
54
55    public static void main(String[] args) {
56        Jedis jedis = new Jedis("localhost", 6379);
57        BloomFilterExample cache = new BloomFilterExample(jedis, 10000, 0.01);
58
59        String userId = "nonexistent"; // 修改这个值来测试存在和不存在的情况
60        String cacheKey = "user:" + userId;
61        int cacheTime = 3600; // 缓存 1 小时
62
63        User user = cache.getCachedData(cacheKey, User.class, () -> {
64            // 模拟数据库查询,返回null表示数据不存在
65            return null;
66        }, cacheTime);
67
68        if (user == null) {
69            System.out.println("User not found.");
70        } else {
71            System.out.println("User: " + user);
72        }
73
74        jedis.close();
75    }
76
77    static class User {
78        private String id;
79        private String name;
80        private String email;
81
82        // Getters and Setters
83
84        @Override
85        public String toString() {
86            return "User{id='" + id + "', name='" + name + "', email='" + email + "'}";
87        }
88    }
89}
90

3. 参数校验

对请求参数进行严格校验,过滤掉明显无效的请求。例如,用户ID应为正整数,可以在程序中对用户ID进行校验,过滤掉不符合规则的请求。

示例代码:

1import redis.clients.jedis.Jedis;
2
3public class ParameterValidationExample {
4    private Jedis jedis;
5
6    public ParameterValidationExample(Jedis jedis) {
7        this.jedis = jedis;
8    }
9
10    public <T> T getCachedData(String key, Class<T> clazz, DataProvider<T> provider, int cacheTime) {
11        try {
12            if (!isValidKey(key)) {
13                return null;
14            }
15
16            String cacheValue = jedis.get(key);
17            if (cacheValue != null) {
18                if (cacheValue.equals("null")) {
19                    return null;
20                }
21                return objectMapper.readValue(cacheValue, clazz);
22            }
23
24            T data = provider.getData();
25            if (data == null) {
26                jedis.setex(key, 300, "null"); // 缓存空结果5分钟
27            } else {
28                jedis.setex(key, cacheTime, objectMapper.writeValueAsString(data));
29            }
30            return data;
31        } catch (Exception e) {
32            e.printStackTrace();
33            return null;
34        }
35    }
36
37    private boolean isValidKey(String key) {
38        // 仅示例性校验,实际中应视具体情况进行校验
39        return key != null && key.matches("^user:\\d+$");
40    }
41
42    public interface DataProvider<T> {
43        T getData();
44    }
45
46    public static void main(String[] args) {
47        Jedis jedis = new Jedis("localhost", 6379);
48        ParameterValidationExample cache = new ParameterValidationExample(jedis);
49
50        String userId = "123"; // 正确的用户ID
51        String cacheKey = "user:" + userId;
52        int cacheTime = 3600; // 缓存 1 小时
53
54        User user = cache.getCachedData(cacheKey, User.class, () -> {
55            // 模拟数据库查询
56            return getUserFromDatabase(userId);
57        }, cacheTime);
58
59        if (user == null) {
60            System.out.println("User not found.");
61        } else {
62            System.out.println("User: " + user);
63        }
64
65        jedis.close();
66    }
67
68    private static User getUserFromDatabase(String userId) {
69        // 模拟数据库查询
70        User user = new User();
71        user.setId(userId);
72        user.setName("John Doe");
73        user.setEmail("[email protected]");
74        return user;
75    }
76
77    static class User {
78        private String id;
79        private String name;
80        private String email;
81
82        // Getters and Setters
83
84        @Override
85        public String toString() {
86            return "User{id='" + id + "', name='" + name + "', email='" + email + "'}";
87        }
88    }
89}
90

总结

通过以上示例代码,您可以分别使用缓存空结果、布隆过滤器和参数校验的方法来解决Redis的缓存穿透问题。选择合适的方法可以有效地减少对数据库的无效访问,提高系统的性能和稳定性。


Redis(80)如何解决Redis的缓存穿透问题?》 是转载文章,点击查看原文


相关推荐


零代码改造 + 全链路追踪!Spring AI 最新可观测性详细解读
阿里云云原生2025/10/22

作者:张铭辉(希铭) 本文部分总结于 8 月的 LoongSuite 系列公开课及 AI 原生应用实战营 meetup 分享内容。如需查看原视频欢迎移步:developer.aliyun.com/live/255218… 前言:AI Agent 从 Demo 到生产阶段的挑战 自 2022 年底 GPT-3.5 引爆大模型革命以来,AI 应用经历了从技术探索到产业落地的快速演进。开源模型迭代与低代码平台的兴起,推动了 AI Agent 开发效率的显著提升。然而,行业普遍面临一个核心矛盾:绝大多数


DeepSeek OCR:用'眼睛'阅读长文本,AI记忆新纪元?
墨风如雪2025/10/21

嘿,AI圈的朋友们!最近DeepSeek团队又搞了个大动作,发布了一款名叫DeepSeek-OCR的开源模型。但你可别以为这只是一个普通的文字识别工具,它的核心理念简直是脑洞大开,可能会彻底改变我们处理长文本的方式。 想象一下,我们的大语言模型(LLM)在面对海量文本时,常常会因为算力消耗过大而头疼不已,上下文一长,计算量就呈平方级增长。DeepSeek-OCR另辟蹊径,它不直接处理文本,而是巧妙地把文本信息“画”成图像,然后对这些图像进行压缩!是不是有点像人类先看图再理解,而不是一个字一个字地


如何从 iPhone 中导出视频
Digitally2025/10/20

如果你的 iPhone 存储空间不足,或者你想在发布到社交媒体之前编辑视频,你可以将视频从 iPhone 转移到电脑上,以释放更多空间。在 Windows 电脑上,通常使用 iTunes 在电脑和 iPhone 之间传输文件。在 Mac 上,如果你使用的是 macOS Mojave 或更早版本,可以使用 iTunes;如果你使用的是 macOS Catalina 或更高版本,则可以使用 Finder。在本文中,我们将向你展示多种从 iPhone 中导出视频的方法。有些方法需要使用 iTunes,


Pinia 状态管理原理与实战全解析
90后晨仔2025/10/19

一、前言:为什么选择 Pinia? 在 Vue2 时代,我们常用 Vuex 来做全局状态管理。 但是 Vue3 带来了全新的响应式系统(Composition API + Proxy),于是 Vue 官方团队推出了 Pinia —— 一款更轻量、更现代、更易用的状态管理库。 Pinia 的核心理念是: “让状态管理像使用普通变量一样简单。” 相比 Vuex,它具备以下优势: 特点VuexPinia语法基于 Mutation


图解AI核心技术:大模型、RAG、智能体、MCP
京东云开发者2025/10/17

简介 本文整理了来自Daily Dose of Data Science最热门或最新的文章,其中极具特色的动图以生动形象的方式,帮助我们更好的理解AI中的一些核心技术,希望能够帮助大家更好的理解和使用AI。 大模型 Transformer vs. Mixture of Experts 混合专家 (MoE) 是一种流行的架构,它使用不同的“专家”来改进 Transformer 模型。 下图解释了它们与 Transformers 的区别。 Transformer 使用前馈网络。 MoE 使用专家


一次慢接口背后,竟藏着40+种可能!你中过几个
uzong2025/10/16

从客户端到数据库的全链路性能瓶颈系统性排查指南 本文系统梳理了导致接口响应缓慢的40余种潜在原因,涵盖网络、网关、JVM、数据库、中间件等多个层面,旨在帮助开发者建立完整的性能问题排查思维模型,适用于线上故障定位与技术面试准备 作者:面汤放盐(公众号) || uzong 时间:2025-10-15 转载请备注声明 为什么会问这个问题 问题:现在生产上有一个慢接口,请分析一下这个慢接口,可能是系统中哪些环节导致的,大致原因是什么,可以是一些很细的案例,系统性的分析一下,越多越好,注意仅从理论


本地搭建 Jekyll 环境
爪娃侠2025/10/15

1、安装依赖 Windows:安装RubyInstaller(勾选「Add Ruby to PATH」)Mac:自带 Ruby,需更新 gem:sudo gem update --systemLinux:sudo apt-get install ruby-full 我个人Mac电脑安装时出现版本报错: 原因: macOS 系统自带的 Ruby 版本过低(当前是 2.6.10),而新版rubygems-update要求 Ruby 版本≥3.2.0,导致直接更新失败。 解决:用Ruby 版本管理工具


Hello 算法:让前端人真正理解算法
灵感__idea2025/10/13

每个系列一本前端好书,帮你轻松学重点。 本系列来自上海交通大学硕士,华为高级算法工程师 靳宇栋 的 《Hello,算法》 程序员圈儿有两种怪象: 1、人人称工程师,但少有人能真正担起一项“工程”。 2、掌握算法本是理所应当,实际寥寥无几。 一直以来,算法好像跟前端开发没多少关联,顶多用来应付面试。 本系列要做的,就是同大家一起啃下这块硬骨头,真正理解算法。 算法是什么 算法是什么,没有标准答案。 先看几个实际案例: 查字典 在字典里,每个汉字都对应一个拼音,而字典是按照字母顺序排列的。 查找”


深入浅出 C# MVC:从基础实践到避坑指南(附完整代码示例)
William_cl2025/10/12

目录 1. 引言:C# MVC 为何仍是企业级开发的优选?2. C# MVC 核心知识树(附可视化图谱)3. 实战上手:从零搭建学生管理系统(完整代码)3.1 第一步:创建 MVC 项目3.2 Model 层:定义数据实体与验证规则3.3 Controller 层:处理请求与业务逻辑3.4 View 层:渲染页面与用户交互3.4.1 列表页(Index.cshtml)3.4.2 添加表单页(Create.cshtml) 3.5 运行效果 4. 开发必避:5 个高频 “坑点” 及解


【Node】认识一下Node.js 中的 VM 模块
你的人类朋友2025/10/10

前言 今天介绍 Node.js 中的 VM(Virtual Machine)模块的基本概念和使用方法。 很多人不太了解他,比如在下 所以本文也不会过于深入,会偏向入门! 小目标:看完之后向自己解释一下:啥是 VM 模块?它有什么作用? 什么是 VM 模块 VM 模块是 Node.js 内置的模块,用于在 V8 虚拟机上下文中编译和执行 JavaScript 代码。 说人话就是,VM 模块允许你在隔离的环境中运行 JavaScript 代码。 核心功能 这边用代码进行举例子,后面会介绍具体的使用

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0