Redis(83)Redis的缓存击穿是什么?

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

缓存击穿的概念

缓存击穿(Cache Breakdown)指的是在某一个热点缓存数据过期的瞬间,有大量并发请求同时访问这个数据,而该数据在缓存中不存在,因此所有的请求都打到数据库上,导致数据库压力过大,可能引起系统性能问题。

解决缓存击穿的方法

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

  1. 互斥锁(Mutex):在缓存失效时,只有一个线程去加载数据,其他线程等待。
  2. 永不过期:热点数据的缓存永不过期,只在数据更新时主动去更新缓存。
  3. 预加载:在缓存即将过期之前,提前加载数据到缓存。

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

1. 互斥锁(Mutex)

通过加锁的方式,控制缓存失效时只有一个线程去加载数据,其他线程等待,从而避免大量请求同时打到数据库。

示例代码:

1import redis.clients.jedis.Jedis;
2import redis.clients.jedis.params.SetParams;
3
4public class CacheBreakdownWithLockExample {
5    private Jedis jedis;
6
7    public CacheBreakdownWithLockExample(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        CacheBreakdownWithLockExample cache = new CacheBreakdownWithLockExample(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

2. 永不过期

对于热点数据,设置其缓存为永不过期,只在数据更新时主动去更新缓存。

示例代码:

1import redis.clients.jedis.Jedis;
2
3public class CacheBreakdownWithNoExpireExample {
4    private Jedis jedis;
5
6    public CacheBreakdownWithNoExpireExample(Jedis jedis) {
7        this.jedis = jedis;
8    }
9
10    public void setCachedData(String key, String value) {
11        jedis.set(key, value); // 设置为永不过期
12    }
13
14    public String getCachedData(String key) {
15        return jedis.get(key);
16    }
17
18    public static void main(String[] args) {
19        Jedis jedis = new Jedis("localhost", 6379);
20        CacheBreakdownWithNoExpireExample cache = new CacheBreakdownWithNoExpireExample(jedis);
21
22        String key = "dataKey";
23        String value = "dataValue";
24
25        // 设置缓存数据
26        cache.setCachedData(key, value);
27
28        // 获取缓存数据
29        String cachedValue = cache.getCachedData(key);
30        System.out.println("Cached Value: " + cachedValue);
31
32        jedis.close();
33    }
34}
35

3. 预加载

在缓存即将过期之前,提前加载数据到缓存中,防止缓存失效带来的问题。

示例代码:

1import redis.clients.jedis.Jedis;
2
3import java.util.concurrent.Executors;
4import java.util.concurrent.ScheduledExecutorService;
5import java.util.concurrent.TimeUnit;
6
7public class CacheBreakdownWithPreloadExample {
8    private Jedis jedis;
9
10    public CacheBreakdownWithPreloadExample(Jedis jedis) {
11        this.jedis = jedis;
12    }
13
14    public void setCachedData(String key, String value, int cacheTime) {
15        jedis.setex(key, cacheTime, value);
16    }
17
18    public String getCachedData(String key) {
19        return jedis.get(key);
20    }
21
22    public void preloadCache(String key, DataProvider provider, int cacheTime, int preloadTime) {
23        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
24        scheduler.scheduleAtFixedRate(() -> {
25            String value = provider.getData();
26            if (value != null) {
27                jedis.setex(key, cacheTime, value);
28            }
29        }, 0, preloadTime, TimeUnit.SECONDS);
30    }
31
32    public interface DataProvider {
33        String getData();
34    }
35
36    public static void main(String[] args) {
37        Jedis jedis = new Jedis("localhost", 6379);
38        CacheBreakdownWithPreloadExample cache = new CacheBreakdownWithPreloadExample(jedis);
39
40        String key = "dataKey";
41        int cacheTime = 3600; // 缓存 1 小时
42        int preloadTime = 3500; // 预加载时间 3500 秒
43
44        // 模拟数据预加载
45        cache.preloadCache(key, () -> {
46            // 模拟数据库查询
47            return "dataValue";
48        }, cacheTime, preloadTime);
49
50        // 获取缓存数据
51        String cachedValue = cache.getCachedData(key);
52        System.out.println("Cached Value: " + cachedValue);
53
54        jedis.close();
55    }
56}
57

总结

通过以上示例代码,您可以分别采用互斥锁、永不过期和预加载等方法来解决Redis的缓存击穿问题。合理使用这些方法,可以有效避免热点数据失效时对数据库的瞬时压力,提高系统的稳健性和可用性。


Redis(83)Redis的缓存击穿是什么?》 是转载文章,点击查看原文


相关推荐


从入门到精通:JavaScript异步编程避坑指南
良山有风来2025/10/23

你是不是也遇到过这样的场景?页面上有个按钮,点击后需要先请求数据,然后根据数据更新界面,最后弹出提示框。结果代码写着写着就变成了“回调地狱”,一层套一层,自己都看不懂了。更可怕的是,有时候数据没加载完,页面就显示了,各种undefined错误让人抓狂。 别担心,这篇文章就是来拯救你的。我会带你从最基础的异步概念开始,一步步深入Promise、async/await,最后还会分享几个实战中超级好用的技巧。读完本文,你不仅能彻底理解JavaScript的异步机制,还能写出优雅高效的异步代码。 为什么


Swift 字符串与字符完全导读(三):比较、正则、性能与跨平台实战
unravel20252025/10/22

字符串比较的 3 个层次 比较方式API等价准则复杂度备注字符相等“==”扩展字形簇 canonically equivalentO(n)最常用前缀hasPrefix(:)UTF-8 字节逐段比较O(m)m=前缀长度后缀hasSuffix(:)同上,从后往前O(m)注意字形簇边界 示例 let precomposed = "café" // U+00E9 let decomposed = "


主流DDS实现简介及对比
^Moon^2025/10/20

DDS有多个团体进行过实现,这些实现各有侧重,适用于不同场景(如嵌入式、实时系统、大规模分布式系统等)。以下从开源属性、性能、功能、适用场景等维度进行对比分析: 一、主流DDS实现简介及对比 特性RTI Connext DDSFast DDSADLINK OpenSplice DDSCycloneDDS开发者Real-Time Innovations (RTI)eProsima(西班牙公司)ADLINK Technology(台湾凌华)Eclipse基金会(开源社区)开源属性商业闭源(提供免


Anthropic Haiku 4.5:这波AI性能,我愿称之为“超值”!
墨风如雪2025/10/19

嘿,各位AI圈的朋友们!最近,Anthropic又悄悄地扔出了一颗重磅炸弹——他们最新发布的Claude Haiku 4.5,可不是那种哗众取宠的“大而全”模型,它走的是一条“小、快、灵”的路线,但其带来的性价比和实用性,绝对能让你眼前一亮。在我看来,这不只是一次版本更新,更是AI普惠化进程中一个非常重要的里程碑。 想象一下,你用着一台小型跑车的钱,却买到了一辆豪华轿车的核心动力,甚至速度还更快——Claude Haiku 4.5给人的,就是这样一种惊喜。 小身材,大能量:性能直逼“老大哥” H


Docker快速入门——第四章Docker镜像
温柔一只鬼.2025/10/18

传送门: Docker快速入门——第一章Docker入门 Docker快速入门——第二章Docker基本概念 Docker快速入门——第三章Docker环境安装 一、搜索镜像 在Docker中,通过如下命令搜索镜像: docker search [OPTIONS] TERM 其中TERM是你要搜索的镜像关键词 常用选项(OPTIONS): --limit N:限制返回结果的数量(默认为25,最大为100) --filter"is-oddicial=true":只


【搞发🌸活】不信书上那套理论!亲测Javascript能卡浏览器Reader一辈子~
大怪v2025/10/16

点进来的前端佬,先别走! 让我详细给你逼逼叨! 在很久很久以前,前端圈就广泛流传,Javascript的加载和执行,都会阻塞浏览器Render。 然后过了这些日子,作为一名优秀的前端佬的意识爆发。 按照上面的说法,那是不是可以构造一个Javascript程序,让后续的CSS以及HTML文本永远都不能被解析Render到? 喔,觉的挺来劲的,说干就干! 前言 一开始构建了这么一个HTML,如下: <!DOCTYPE html> <html> <head> <meta charset="UT


算法刷题-数组篇之螺旋矩阵II(超简单)
destiny_tool2025/10/15

力扣题目链接https://leetcode.cn/problems/spiral-matrix-ii/ 1.1 问题描述: 给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。 示例: 输入: 3 输出: [ [ 1, 2, 3 ],                        [ 8, 9, 4 ],                        [ 7, 6, 5 ] ]    1.2 思路: 本题具体考察


Spring Boot 3.x核心特性与性能优化实战
奋斗的小monkey2025/10/14

Spring Boot 3.x核心特性与性能优化实战 前言 随着Java生态的持续演进,Spring Boot 3.x作为最新版本带来了许多重大改进和创新特性。本文将深入探讨Spring Boot 3.x的核心技术亮点,并结合实际案例展示性能优化的最佳实践。 1. 技术栈升级 Spring Boot 3.x正式要求使用JDK 17及以上版本,这标志着Spring框架全面拥抱现代Java特性。 // 传统方式 @Configuration public class AppConfig {


黑马商城微服务项目准备工作并了解什么是微服务、SpringCloud
Le1Yu2025/10/12

目录 一、后端项目的导入以及启动服务的配置 二、前端nginx项目的导入 三、linux虚拟机MySql安装 四、单体架构与微服务         单体架构        :         微服务: 五、SpringCloud 一、后端项目的导入以及启动服务的配置         将资料当中的项目下载下来后用idea打开;按Alt+8打开Services面板,按照指示添加启动项:         找到Spring Boot:         点击后应该


从0到1微调DeepSeek大模型,LoRA+4位量化让24GB显卡也能玩转
陈敬雷-充电了么-CEO兼CTO2025/10/10

注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》(跟我一起学人工智能)【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷】 文章目录 GPT多模态大模型与AI Agent智能体系列二百一十六从0到1微调DeepSeek大模型,LoRA+4位量化让24GB显卡也能玩转一、为什么要微调DeepSeek?从“通才”到“专家”的蜕变二、微调核心原理:从损失函数到数据策略

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0