redis缓存常见问题及解决方案

news/2024/11/5 21:20:23/文章来源:https://www.cnblogs.com/21CHS/p/18528862

redis缓存常见问题及解决方案

1、缓存穿透

缓存穿透: 是指查询一个不存在的数据,由于缓存无法命中,将去查询数据库,但是数据库也无此记录,并且出于容错考虑,我们没有将这次查询的null写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

  • 解决1 :空结果也进行缓存,但它的过期时间会很短,最长不超过五分钟,但是不能防止随机穿透。

  • 解决2 :使用布隆过滤器或者Redis的Bitmap来解决随机穿透问题

Redis的Bitmap解决缓存穿透

  • setbit key offset value:设置或清除指定偏移量上的位(bit)。offset 是从0开始的位索引,value 可以为 0 或 1。
  • getbit key offset:返回指定偏移量上的位值。

实例

public solution(){String key = "sku:product:data";//查询mysql里面商品skuIdList<ProductSku> productSkuList = productSkuMapper.selectList(null);productSkuList.forEach(item -> {//将所有商品的SkUId添加到redis里面的bitmap中redisTemplate.opsForValue().setBit(key,item.getId(),true);});
}// 测试
public void getProductSku(Long skuId) {//调用商品接口之前 提前知道用户访问商品SKUID是否存在于bitmap中String key = "sku:product:data";//根据skuId和可以查询redis中的数据Boolean flag = redisTemplate.opsForValue().getBit(key, skuId);if (!flag) {log.error("用户查询商品sku不存在:{}", skuId);//查询数据不存在直接返回空对象throw new ServiceException("用户查询商品sku不存在");}  
}

注意当数据库商品表进行更新时,bitmap也要及时更新。

2、缓存雪崩

缓存雪崩:是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。

  • 解决1:原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

  • 解决2:如果单节点宕机,可以采用集群部署方式防止雪崩

// 设置随机过期时间
redisTemplate.opsForValue().set(key,value,time, TimeUnit.SECONDS);

3、缓存击穿

缓存击穿: 是指对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:如果这个key在大量请求同时进来之前正好失效,那么所有对这个key的数据查询都落到db,我们称为缓存击穿。

与缓存雪崩的区别:

  1. 击穿是一个热点key失效
  2. 雪崩是很多key集体失效

解决:加锁

当一些key在大量请求同时进来之前正好失效,那么我们需要加锁,只放行一个请求去数据库查询,并把查询到的结果缓存到redis中。后面其他请求进来时都从redis中快速获取数据。

进程内锁:synchronized和lock锁

不能解决多进程之间的多线程并发问题。

public synchronized void testLock() {// 查询Redis中的num值String value = (String)this.stringRedisTemplate.opsForValue().get("num");// 没有该值returnif (StringUtils.isBlank(value)){return ;}// 有值就转成成intint num = Integer.parseInt(value);// 把Redis中的num值+1this.stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));
}

进程外锁:分布式锁

分布式锁主流的实现方案:

  1. 基于数据库实现分布式锁
  2. 基于缓存( Redis等)
  3. 基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点:

  1. 高性能:Redis最高
  2. 可靠性:zookeeper最高

分布式锁使用的逻辑如下:

尝试获取锁成功:执行业务代码    执行业务  try{获取锁业务代码-宕机} catch(){}finally{ 释放锁}失败:等待(回旋);

代码

/*** 采用SpringDataRedis实现分布式锁* 原理:执行业务方法前先尝试获取锁(setnx存入key val),如果获取锁成功再执行业务代码,业务执行完毕后将锁释放(del key)*/
public void testLock() {//0.先尝试获取锁 setnx key valBoolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");if(flag){//获取锁成功,执行业务代码//1.先从redis中通过key num获取值  key提前手动设置 num 初始值:0String value = stringRedisTemplate.opsForValue().get("num");//2.如果值为空则非法直接返回即可if (StringUtils.isBlank(value)) {return;}//3.对num值进行自增加一int num = Integer.parseInt(value);stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));//4.将锁释放stringRedisTemplate.delete("lock");}else{try {Thread.sleep(100);this.testLock();} catch (InterruptedException e) {e.printStackTrace();}}
}

4、数据一致性

在当前环境下,通常我们会首选redis缓存来减轻我们数据库访问压力。但是也会遇到以下这种情况:大量用户来访问我们系统,首先会去查询缓存, 如果缓存中没有数据,则去查询数据库,然后更新数据到缓存中,并且如果数据库中的数据发生了改变则需要同步到redis中,同步过程中需要保证 MySQL与redis数据一致性问题

解决1:使用延时双删策略

延时双删策略是一种常见的保证MySQL和Redis数据一致性的方法。其主要流程包括:先删除缓存,然后更新数据库。这个过程完成后,大约在数据库从库更新后再次删除缓存。具体的步骤如下:

第一步,先执行redis.del(key)操作删除缓存;

第二步,然后执行写数据库的操作;

第三步,休眠一段时间(例如500毫秒),根据具体的业务时间来定;

第四步,再次执行redis.del(key)操作删除缓存。

延时双删策略通过这种方式尝试达到最终的数据一致性,但是这并不是强一致性,因为MySQL和Redis主从节点数据的同步并不是实时的,所以需要等待一段时间以增强它们的数据一致性。同时,由于读写是并发的,可能出现缓存和数据库数据不一致的问题

//修改
@Transactional
@Override
public int updateProduct(Product product) {//1 删除缓存(获取spu下的sku id列表)List<Long> skuIdList =  product.getProductSkuList().stream().map(ProductSku::getId).collect(Collectors.toList());//从redis中删除每个sku的缓存skuIdList.forEach(skuId -> {String dataKey = "product:sku:" + skuId;this.redisTemplate.delete(dataKey);});//2 之前的业务代码,执行更新商品操作.....//3 休眠一段时间try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}//4 再次执行操作删除缓存skuIdList.forEach(skuId -> {String dataKey = "product:sku:" + skuId;this.redisTemplate.delete(dataKey);});return 1;
}

解决2:使用canal解决

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI辅助动画制作,现实到虚拟仅需要一个摄像头。多种AI技术融合赋能传统行业,或是产业趋势?

AI辅助动画制作,现实到虚拟仅需要一个摄像头。图源:youtube authour autodesk media & entertainment 不是元宇宙,是动画。 2024年10.30日。美国加利福尼亚公司 Wonder dynamic 发布了最新的产品视频。只需要一个摄像头,我们可以把所有的一切搬到虚拟世界。此产品利用多…

chapter14

第一题问题首先,编写一个名为 null.c 的简单程序,它创建一个指向整数的指针,将其设置为NULL,然后尝试对其进行释放内存操作。把它编译成一个名为 null 的可执行文件。当你运行这个程序时会发生什么?自己写的输出如下:无任何输出或错误提示。 第二题问题接下来,编译该程序…

0XGAME [Week 3] 重生之我在南邮当CTF大王

0XGAME [Week 3] 重生之我在南邮当CTF大王 新尝试:源文件找线索;新知识:兽音加密 下载是个游戏和源代码,玩了以下,感觉答对问题也是可以得到flag,但是感觉耗时,而且应该有藏flag的地方,在一堆文件里面找,data文件夹里面的4个地图json文件,进去发现了flag字眼是个2,那…

sentinel微服务限流

sentinel(微服务限流) 官网地址:https://sentinelguard.io/zh-cn/ 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是由阿里巴巴开源的一款流量防护组件,Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 微服…

RabbitMQ工作模式

RabbitMQ工作模式RabbitMQ提供了多种工作模式:简单模式,work模式 ,Publish/Subscribe发布与订阅模式,Routing路由模式,Topics主题模式等官网对应模式介绍:https://www.rabbitmq.com/getstarted.html 1、简单模式 生产者直接发送消息到队列上(虽然没有指明使用交换机,但是…

RabbitMQ消息幂等性保障

消息幂等性保障幂等性指一次和多次请求某一个资源,对于资源本身应该具有同样的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。在MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果消息幂等性保障 乐观锁机制 @Component public class …

WPF Button控件 这里可以点一下

WPF Button控件 这里可以点一下button表示一个 Windows 按钮控件,该控件对 Click 事件做出反应。 可以点一下button,执行程序操作,如:显示对话框,更改显示内容。 button的content属性表示按钮上显示的文字。<StackPanel><!-- 一个按钮控件,太小了,根本就看不到…

WPF Textbox控件 这里可以输入文字

WPF Textbox控件 这里可以输入文字 textbox控件,用于输入文字。如网页上输入账号密码的地方就是文本框。 文本框的text属性可以提示文字,只能包含无格式文本。

蛋白粉?蛋白质

蛋白粉不能用开水冲,但我们摄入的蛋白质却大都经过了烹煮。 为什么蛋白质不怕开水,而蛋白粉怕开水? 这似乎是矛盾的,其实不然。 问题是,很多人并不了解蛋白质的分子结构,不了解蛋白粉的溶解原理。 如果不了解其中的原理,很容易给出错误的解释。咳咳咳~~干货内容一次可…

【考试题解】多校A层冲刺NOIP2024模拟赛18

目录A. 选彩笔(rgb)题目内容部分分正解思路代码B. 兵蚁排序(sort)题目内容部分分75pts正解思路代码C. 人口局DBA(dba)题目内容部分分60pts正解思路代码D. 银行的源起(banking) A. 选彩笔(rgb) 题目内容 有 \(N\) 支彩笔,每支彩笔有 \(R_i,G_i,B_i\) 三个属性。定义两只彩笔 \(…

学习笔记(二十五):ArkUi-栅格布局 (GridRow/GridCol)

概述: 栅格布局是一种通用的辅助定位工具,对移动设备的界面设计有较好的借鉴作用。主要优势包括:提供可循的规律:栅格布局可以为布局提供规律性的结构,解决多尺寸多设备的动态布局问题。通过将页面划分为等宽的列数和行数,可以方便地对页面元素进行定位和排版。统一的定位…