当前位置: 首页 > news >正文

Redis缓存解决方案

Redis缓存

查询数据

操作步骤:

在这里插入图片描述
问题:在数据库更新后、缓存失效前存在短暂不一致期


更新数据

操作步骤:

  1. 更新数据库
  2. 删除缓存中的对应数据
  3. 下次读取时自动重新加载最新数据到缓存

存在的问题:
在这里插入图片描述
通常建议"先更新数据库,再删除缓存"
反过来可能导致更长时间的不一致
解决方案:
设置较短的缓存时间,尽量避免长时间数据不一致问题


插入数据

操作步骤:
仅插入数据库
不需要操作Redis缓存
缓存预热:

  1. 对于重要的新数据,可以主动写入缓存
  2. 对于可能被频繁访问的新数据,设置较长的过期时间

删除数据

操作步骤:

  1. 先删除数据库记录
  2. 再删除缓存

演示数据

DROP TABLE IF EXISTS `products`;
CREATE TABLE `products`  (`product_id` int NOT NULL AUTO_INCREMENT,`product_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`category` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`price` decimal(10, 2) NOT NULL,`stock_quantity` int NOT NULL DEFAULT 0,`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`product_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of products
-- ----------------------------
INSERT INTO `products` VALUES (1, 'iPhone 15', '智能手机', 2999.00, 100, '苹果最新款智能手机,A16芯片,超视网膜XDR显示屏', '2025-07-12 23:51:28', '2025-07-12 23:51:28');
INSERT INTO `products` VALUES (2, '小米电视65英寸', '家用电器', 3299.00, 50, '4K超高清,MEMC运动补偿,全面屏设计', '2025-07-12 23:51:28', '2025-07-12 23:51:28');
INSERT INTO `products` VALUES (3, '耐克Air Max运动鞋', '运动鞋服', 899.00, 200, '经典气垫设计,舒适缓震,多种配色可选', '2025-07-12 23:51:28', '2025-07-12 23:51:28');

缓存问题

缓存穿透

大量请求查询数据库中不存在的数据,导致请求直接打到数据库上
缓存穿透解决方案:

  1. 缓存空对象
    对查询结果为null的数据也进行缓存,设置较短的过期时间
	@GetMapping("getNull")public Result getNull(@RequestParam Integer pid){//redis keyString key = PRODUCT_PRE+pid;//1.redis查询Products products = (Products)redisTemplate.opsForValue().get(key);//如果redis没查到去数据库查询if(products != null || redisTemplate.hasKey(key)) //数据不等于空或者key是存在的{return Result.ok("查询成功",products);}//查到数据就将数据保存到redisQueryWrapper<Products> qw = new QueryWrapper<>();qw.eq("product_id",pid);Products one = productsService.getOne(qw);//添加过期时间,防止读到旧数据redisTemplate.opsForValue().set(key,one,10, TimeUnit.MINUTES);//设置10分钟过期return Result.ok("查询成功",one);}
  1. 布隆过滤器
    在缓存前加一层布隆过滤器
    快速判断数据是否存在,不存在则直接返回
    布隆过滤器如果判断不存在一定不存在,如果判断存在不一定存在
    优点:
    空间效率极高:不需要存储元素本身
    查询时间快:O(k)时间复杂度(k通常很小)
    安全:不存储原始数据,适合敏感数据场景
    缺点:
    存在误判(False Positive):可能判断存在的元素实际不存在
    不能删除元素:标准布隆过滤器不支持删除操作
    不能获取元素:仅能判断存在性,不能获取元素内容

缓存击穿

问题描述:热点key过期瞬间,大量请求直接打到数据库

  1. 设置热点数据永不过期
    物理不过期:不设置TTL过期时间
    逻辑过期:值中包含过期时间字段,异步刷新

创建RedisDataExpire记录过期时间

@Data
public class RedisDataExpire 
{private Object o;private LocalDateTime expire;
}
	private static final String LOCK = "shop:lock:"; @GetMapping("expireGet")public Result expireGet(@RequestParam Integer pid){//redis keyString key = PRODUCT_PRE+pid;//1.redis查询RedisDataExpire redisDataExpire = (RedisDataExpire)redisTemplate.opsForValue().get(key);//如果redis没查到去数据库查询//数据为空或者数据过期if(redisDataExpire == null || redisDataExpire.getExpire().isBefore(LocalDateTime.now())){String lockKey = LOCK + pid;//上锁if(redisTemplate.opsForValue().setIfAbsent(lockKey, "lock", 60, TimeUnit.SECONDS)){//异步处理Thread t = new Thread(){@Overridepublic void run() {try {//查询数据库QueryWrapper<Products> qw = new QueryWrapper<>();qw.eq("product_id",pid);Products one = productsService.getOne(qw);if(one == null){throw new RuntimeException("数据不存在");}//创建对象,将数据库查到的对象放进去RedisDataExpire data = new RedisDataExpire();data.setO(one);data.setExpire(LocalDateTime.now().plusSeconds(30));//设置30秒后过期redisTemplate.opsForValue().set(key,data);//设置10分钟过期}finally {redisTemplate.delete(lockKey);//解锁}}};t.start();//线程开始}}//如果为空就返回空数据,如果不为空就返回Productsreturn Result.ok("查询成功",redisDataExpire==null?null:redisDataExpire.getO());}
  1. 互斥锁(Mutex Lock):
	private static final String LOCK = "shop:lock:"; @GetMapping("mutexGet")public Result mutexGet(@RequestParam Integer pid) {//redis keyString key = PRODUCT_PRE+pid;//1.redis查询Products products;while( (products = (Products)redisTemplate.opsForValue().get(key)) == null) //如果查不到数据就阻塞在这里{//上锁的keyString lockKey = LOCK + pid;//设置过期时间,防止死锁//上锁,将其它线程阻塞、if (redisTemplate.opsForValue().setIfAbsent(lockKey, "lock", 60, TimeUnit.SECONDS)) {try {//查询数据库QueryWrapper<Products> qw = new QueryWrapper<>();qw.eq("product_id", pid);Products one = productsService.getOne(qw);if (one == null) {throw new RuntimeException("数据不存在");}redisTemplate.opsForValue().set(key, one, 10, TimeUnit.MINUTES);//设置10分钟过期}finally {redisTemplate.delete(lockKey);//解锁}}}return Result.ok("查询成功",products);}
  1. 缓存预热:
    系统启动时加载热点数据

缓存雪崩

问题描述:大量缓存同时失效,导致所有请求直接访问数据库

缓存穿透解决方案:

  1. 过期时间随机化
  2. 高可用架构:
    Redis集群部署
    哨兵模式或Cluster模式保证高可用
  3. 熔断降级机制:
    当数据库压力过大时,返回降级数据或默认值

项目Demo

https://gitee.com/xi-ri/cache-redis

http://www.dtcms.com/a/279251.html

相关文章:

  • Laravel 中 chunk 分页漏掉数据?深度解析原因与解决方案
  • 深度剖析:动态接口代理核心原理与高级应用
  • 工业4.0时代的安全管理:2025年物联网与AI技术的融合与10+工具实践
  • NSSCTF Web 一点学习
  • 高安全前端架构:Rust-WASM 黑盒技术揭秘
  • 机器学习、深度学习、神经网络之间的关系
  • Binder 概述
  • Linux操作系统从入门到实战(七)详细讲解编辑器Vim
  • 第二章 uniapp实现兼容多端的树状族谱关系图,封装tree-item子组件
  • 自学鸿蒙测试day0
  • 专题:2025机器人产业深度洞察报告|附136份报告PDF与数据下载
  • UDP协议的端口161怎么检测连通性
  • uniapp video视频全屏播放后退出,页面字体变大,样式混乱问题
  • 基于微信小程序停车场车位预约系统的设计与实现
  • 基于微信小程序的财务管理系统的设计与实现;账本管理系统的设计与实现
  • Browser MCP
  • 【PY32】如何使用 J-Link 和 MDK 开发调试 PY32 MCU
  • 第十九篇 自动化报表生成:Python一键生成可视化Excel图表与专业PDF报告,老板看了都点赞!
  • iOS 抓包工具评测:功能、限制与真实开发场景全解析
  • Spark SQL 之 UT
  • 人工智能在气候变化应对中的战略角色:从感知、模拟到决策支持
  • JAVA面试宝典 -《Spring Cloud Alibaba 实战:从限流到熔断》
  • AI多因子模型解析黄金3370美元:避险需求驱动与美欧墨关税升级的联动效应
  • 即刻开发:接入淘宝关键词搜索 API 采集海量商品数据
  • Linux 0.11 中,磁盘分区信息
  • win10安装Elasticsearch
  • 学习C++、QT---24(QT实现记事本项目的打开、保存、关闭)
  • 内测分发平台应用的异地容灾和负载均衡处理和实现思路
  • gitignore添加后如何生效?
  • docker简介