Redis(82)如何解决Redis的缓存雪崩问题?

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

缓存雪崩的概念

缓存雪崩(Cache Avalanche)指的是在某一时刻大量缓存数据同时过期或者缓存服务器宕机,导致大量请求直接打到数据库,从而使数据库瞬时压力剧增,甚至可能导致数据库崩溃。

解决缓存雪崩问题的方法

为了解决缓存雪崩问题,可以采取以下策略:

  1. 缓存数据的过期时间设置为随机值:避免在同一时间大量缓存数据同时失效。
  2. 加锁或队列:在缓存失效时,通过机制控制对数据库的访问,避免大量请求同时打到数据库。
  3. 双写策略:更新缓存的同时也更新数据库,保证数据的一致性。
  4. 数据预热:在系统启动时,预先将一些热点数据加载到缓存中,防止缓存雪崩。

以下是这几种解决方法的详细代码示例:

1. 缓存数据的过期时间设置为随机值

通过设置随机的过期时间,可以避免大量缓存数据在同一时间失效。

示例代码:

1import redis.clients.jedis.Jedis;
2
3public class CacheAvalancheExample {
4    private Jedis jedis;
5
6    public CacheAvalancheExample(Jedis jedis) {
7        this.jedis = jedis;
8    }
9
10    public void setCachedData(String key, String value, int baseExpTime) {
11        int expTime = baseExpTime + (int)(Math.random() * baseExpTime); // 随机过期时间
12        jedis.setex(key, expTime, value);
13    }
14
15    public String getCachedData(String key) {
16        return jedis.get(key);
17    }
18
19    public static void main(String[] args) {
20        Jedis jedis = new Jedis("localhost", 6379);
21        CacheAvalancheExample cache = new CacheAvalancheExample(jedis);
22
23        String key = "dataKey";
24        String value = "dataValue";
25        int baseExpTime = 3600; // 基础过期时间 1 小时
26
27        cache.setCachedData(key, value, baseExpTime);
28
29        String cachedValue = cache.getCachedData(key);
30        System.out.println("Cached Value: " + cachedValue);
31
32        jedis.close();
33    }
34}
35

2. 加锁或队列

通过加锁或者队列的方式,控制缓存失效时对数据库的访问,避免大量请求同时打到数据库。

示例代码:

1import redis.clients.jedis.Jedis;
2import redis.clients.jedis.params.SetParams;
3
4public class CacheAvalancheWithLockExample {
5    private Jedis jedis;
6
7    public CacheAvalancheWithLockExample(Jedis jedis) {
8        this.jedis = jedis;
9    }
10
11    public String getCachedData(String key, DataProvider provider, int cacheTime) {
12        String value = jedis.get(key);
13        if (value != null) {
14            return value;
15        }
16
17        String lockKey = key + ":lock";
18        String requestId = String.valueOf(Thread.currentThread().getId());
19
20        // 尝试加锁
21        boolean locked = tryGetLock(lockKey, requestId, 30000); // 锁定30秒
22        if (locked) {
23            try {
24                value = provider.getData();
25                if (value != null) {
26                    jedis.setex(key, cacheTime, value);
27                }
28            } finally {
29                releaseLock(lockKey, requestId);
30            }
31        } else {
32            // 等待一段时间后重试
33            try {
34                Thread.sleep(100);
35            } catch (InterruptedException e) {
36                e.printStackTrace();
37            }
38            return getCachedData(key, provider, cacheTime);
39        }
40        return value;
41    }
42
43    private boolean tryGetLock(String lockKey, String requestId, int expireTime) {
44        SetParams params = new SetParams().nx().px(expireTime);
45        String result = jedis.set(lockKey, requestId, params);
46        return "OK".equals(result);
47    }
48
49    private void releaseLock(String lockKey, String requestId) {
50        if (requestId.equals(jedis.get(lockKey))) {
51            jedis.del(lockKey);
52        }
53    }
54
55    public interface DataProvider {
56        String getData();
57    }
58
59    public static void main(String[] args) {
60        Jedis jedis = new Jedis("localhost", 6379);
61        CacheAvalancheWithLockExample cache = new CacheAvalancheWithLockExample(jedis);
62
63        String key = "dataKey";
64        int cacheTime = 3600; // 缓存 1 小时
65
66        String value = cache.getCachedData(key, () -> {
67            // 模拟数据库查询
68            return "dataValue";
69        }, cacheTime);
70
71        System.out.println("Cached Value: " + value);
72
73        jedis.close();
74    }
75}
76

3. 双写策略

更新数据库的同时也更新缓存,可以保证缓存的一致性,减小缓存失效的几率。

示例代码:

1import redis.clients.jedis.Jedis;
2
3public class CacheAvalancheWithDoubleWriteExample {
4    private Jedis jedis;
5
6    public CacheAvalancheWithDoubleWriteExample(Jedis jedis) {
7        this.jedis = jedis;
8    }
9
10    public void updateData(String key, String value, int cacheTime) {
11        // 更新数据库
12        updateDatabase(key, value);
13
14        // 更新缓存
15        jedis.setex(key, cacheTime, value);
16    }
17
18    private void updateDatabase(String key, String value) {
19        // 模拟数据库更新操作
20        System.out.println("Database updated: " + key + " = " + value);
21    }
22
23    public String getCachedData(String key) {
24        return jedis.get(key);
25    }
26
27    public static void main(String[] args) {
28        Jedis jedis = new Jedis("localhost", 6379);
29        CacheAvalancheWithDoubleWriteExample cache = new CacheAvalancheWithDoubleWriteExample(jedis);
30
31        String key = "dataKey";
32        String value = "dataValue";
33        int cacheTime = 3600; // 缓存 1 小时
34
35        cache.updateData(key, value, cacheTime);
36
37        String cachedValue = cache.getCachedData(key);
38        System.out.println("Cached Value: " + cachedValue);
39
40        jedis.close();
41    }
42}
43

4. 数据预热

在系统启动时预先将一些热点数据加载到缓存中,可以有效防止缓存雪崩。

示例代码:

1import redis.clients.jedis.Jedis;
2
3public class CacheWarmUpExample {
4    private Jedis jedis;
5
6    public CacheWarmUpExample(Jedis jedis) {
7        this.jedis = jedis;
8    }
9
10    public void warmUpCache() {
11        // 预先加载热点数据到缓存中
12        String key = "hotKey";
13        String value = "hotValue";
14        int cacheTime = 3600; // 缓存 1 小时
15        jedis.setex(key, cacheTime, value);
16
17        System.out.println("Cache warmed up: " + key + " = " + value);
18    }
19
20    public String getCachedData(String key) {
21        return jedis.get(key);
22    }
23
24    public static void main(String[] args) {
25        Jedis jedis = new Jedis("localhost", 6379);
26        CacheWarmUpExample cache = new CacheWarmUpExample(jedis);
27
28        cache.warmUpCache();
29
30        String key = "hotKey";
31        String cachedValue = cache.getCachedData(key);
32        System.out.println("Cached Value: " + cachedValue);
33
34        jedis.close();
35    }
36}
37

总结

通过以上示例代码,您可以分别采用随机过期时间、加锁或队列、双写策略和数据预热等方法来解决Redis的缓存雪崩问题。合理使用这些方法,可以有效地减少对数据库的瞬时压力,提高系统的稳健性和可用性。


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


相关推荐


猿辅导MySQL面试常见问题解析(一)
360_go_php2025/10/23

​ 在进行MySQL相关的面试时,尤其是在如猿辅导这样注重技术底层实现的公司,面试官往往会问一些关于数据库优化、事务管理、锁机制等方面的问题。以下是一些常见的MySQL面试问题及其详细解答,帮助你更好地准备面试。​编辑 1. MySQL建立索引的原则​编辑 在MySQL中,索引是提升查询性能的重要工具。然而,索引虽然能加速查询,但也会占用额外的空间,并且会影响写操作的性能。因此,建立索引时需要遵循一些原则: 选择性高的列:选择性高的列,指的是该列的唯一值多,数据分布较为均匀。对于这些列建立索


Swift 字符串与字符完全导读(二):Unicode 视图、索引系统与内存陷阱
unravel20252025/10/22

Unicode 的三种编码视图 Swift 把同一个 String 暴露成 4 种迭代方式: 视图元素类型单位长度典型用途StringCharacter人眼“一个字符”业务逻辑utf8UInt81~4 字节网络/文件 UTF-8 流utf16UInt162 或 4 字节与 Foundation / Objective-C 交互unicodeScalarsUnicodeScalar21-bit精确到标量,做编码分析 代码一览 l


AWS EKS 集成Load Balancer Controller 对外暴露互联网可访问API [AWS 中国宁夏区]
thinktik2025/10/20

本文主要介绍AWS EKS和AWS Elastic Load Balancing的集成;我们可以通过AWS ELB将运行在EKS中的服务暴露出去,供互联网访问。 AWS ELB提供的负载均衡,高可用,按流量自动弹性和自带的安全性服务(比如基础版的AWS Shield)给为我们的网络服务提供了高质量的保证。虽然我们也可以直接让K8S Service服务直接以public ip的方式来对外服务,但是一般技术和成本等综合来考虑不如AWS ELB。 如下图,AWS ELB充当运行在AWS上的动态API服


AI环境下的网络安全人才的发展方向
Mr_Meng_De2025/10/19

2025年9月16日,2025年国家网络安全宣传周分论坛上发布《AI时代网络安全产业人才发展报告(2025)》。 《报告》由工业和信息化部教育与考试中心、安恒信息、中国联合网络通信有限公司软件研究院、全国数字安全行业产教融合共同体、中国网络空间新兴技术安全创新论坛、智联招聘、中国网络空间安全人才教育论坛联合编制。 AI驱动网络安全领域岗位革新与挑战升级 随着人工智能技术与网络安全行业的深度融合,当代大学生对这一技术变革带来的就业影响形成了较为全面的认知。约三分之一(33.5%)的学生表现出


策略模式:让算法选择像点菜一样简单
太过平凡的小蚂蚁2025/10/18

什么是策略模式? ​策略模式(Strategy Pattern)​​ 是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。 简单来说:​定义策略家族,让客户端自由选择。​​ 现实世界类比 想象你去餐厅吃饭: ​策略接口​:点菜这个行为 ​具体策略​:中餐、西餐、日料等不同菜系 ​上下文​:餐厅(提供点菜环境) ​客户端​:你(根据心情选择今天吃什么) 模式结构 classDiagra


VSCODE GDB调试
Gary Studio2025/10/16

流程 1.首先点击左侧的小虫子按钮 2.选择gdb调试 3.在program一栏填上你需要调试的可执行文件 注意:编译的时候记得在编译命令的最后或者在前面的语句中添加-g 增加调试的选项 例如下面 PS D:\CODE\c> gcc .\04_example2.c -o .\04_example2 -g 4.在需要调试的代码打上断点即可开启运行。 补充 有时候运行不起来有可能是没有安装gdb或者是gdb的环境变量没有配置造成的 验证gdb是否安装 输入以下指令


SpringBoot实现JWT动态密钥轮换
风象南2025/10/15

背景:为什么 JWT 密钥也要"轮换" JWT(JSON Web Token) 是当代认证体系的常用方案, 无论是单体系统、微服务、还是前后端分离登录,几乎都会用到它。 但在大多数系统里,签名密钥往往是一成不变的—— 一旦生成,常年不换,代码里写死或放在配置文件中。 这其实非常危险: 一旦密钥被误传或泄露,攻击者就能伪造任意用户的合法 Token 无论是测试环境误配置,还是日志误打出 key,都可能导致密钥泄露,带来安全隐患 于是我们面临一个工程问题: "如何能动态更新 JWT 签名密钥,


从Java集合到云原生现代数据管理的演进之路
kkjt01302025/10/14

从Java集合到云原生现代数据管理的演进之路在软件开发的漫长历程中,数据管理技术的演进一直是推动应用架构变革的核心动力。从最初在单一应用内存中操作的Java集合,到今天跨越全球分布式环境的云原生数据平台,这条路不仅反映了技术的飞跃,更体现了我们对数据处理能力、规模和敏捷性不断增长的需求。这一演进是渐进而深刻的,每一步都为了解决特定时代的核心挑战。Java集合:单机时代的基石Java集合框架(Java Collections Framework, JCF)是早期Java应用数据管理的基石。它以Li


如何为Spring Cloud Gateway配置具体的负载均衡策略?
IT橘子皮2025/10/12

以下是针对 ​Spring Cloud Gateway​ 配置具体负载均衡策略(如加权轮询、最小连接数等)的详细指南,结合最新技术文档和最佳实践: 一、基础配置与依赖 1. ​引入依赖​ 确保项目中已包含 Spring Cloud Gateway 和负载均衡器(Spring Cloud LoadBalancer)的依赖: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cl


零基础学JavaScript:手把手带你搭建环境,写出第一个程序!
良山有风来2025/10/11

开头:你是不是也遇到过这些问题? 刚学JavaScript的时候,你是不是一脸懵? 打开教程,满屏的“Node.js”、“npm”、“VS Code”,完全不知道从哪下手? 照着网上的教程配置环境,结果各种报错,心态爆炸? 写了半天代码,连个“Hello World”都显示不出来? 别担心!这篇文章就是为你准备的。 我会用最直白的方式,带你一步步搭建JavaScript开发环境,并写出你的第一个程序。 看完这篇文章,你不仅能顺利运行第一个JavaScript程序,还能理解背后的原理,为后续学习打

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0