Redis缓存穿透、击穿、雪崩,数据库与缓存一致性
Redis作为高性能非关系型(NoSQL)的键值对数据库,受到了广大用户的喜爱和使用,大家在项目中都用到了Redis来做数据缓存,但有些问题我们在使用中不得不考虑,其中典型的问题就是:缓存穿透、缓存雪崩、缓存击穿和与关系型数据库的一致性。
一、缓存穿透
1、概念
缓存穿透是指查询一个缓存和数据库不存在的数据
。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。
大致流程如下图所示:
在一些特定场景 例如:秒杀活动
。同一时刻会有大量的请求,都在秒杀同一件商品,这些请求同时去查缓存中没有数据,然后又同时访问数据库。结果悲剧了,数据库可能扛不住压力,直接挂掉。
也会存在有人恶意请求。一般我们的主键ID都是无符号的自增类型,有些人想要搞垮你的数据库,每次请求都用负数ID,而ID为负数的记录在数据库根本就没有。就会每次都去查询数据库,而每次查询都是空,每次又都不会进行缓存。假如有恶意攻击,就可以利用这个漏洞,对数据库造成压力,甚至压垮数据库。
3、解决方案
1) 验证拦截
接口层进行校验,如鉴定用户权限,对ID之类的字段做基础的校验,如id<=0的字段直接拦截。
2) 布隆过滤器
我们可以提前将真实正确的商品id,添加到过滤器当中,每次再进行查询时,先确认要查询的id是否在过滤器当中,如果不在,则说明id为非法id,则不需要进行后续的查询步骤了。
布隆过滤器是一种比较独特数据结构,有一定的误差。布隆过滤器的特点就是 如果它说不存在那肯定不存在,如果它说存在,那数据有可能实际不存在
。
它最大的优点就是性能高,空间占用率及小。
3) 缓存空对象
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源。
但是这种方法会存在两个问题:
- 如果空值能够被缓存起来,这就意味着
缓存需要更多的空间存储更多的键
,因为这当中可能会有很多的空值的键; - 即使对空值设置了过期时间,还是会存在
缓存层和存储层的数据会有一段时间窗口的不一致
,这对于需要保持一致性的业务会有影响。
二、缓存击穿
1、概念
缓存击穿,是指缓存中没有但数据库中有的数据
,并且某一个key非常热点
,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key缓存时间到期,持续的大并发就穿破缓存,直接请求数据库,导致压垮数据库。
2、解决方案
1)设置热点数据永远不过期。
这个方法就比较粗暴,,如果你的热点数据要求实时性比较低,那么可以设置热点数据在热点时段不过期
,在访问低峰期过期,比如每天凌晨过期。
2) 使用分布式锁
互斥锁可以控制查询数据库的线程访问,但这种方案会导致系统的吞吐量下降,需要根据实际情况使用。
public static String getData(String key) throws InterruptedException {
//从Redis查询数据
String result = getDataByKV(key);
//参数校验
if (StringUtils.isBlank(result)) {
try {
//获得锁
if (reenLock.tryLock()) {
//去数据库查询
result = getDataByDB(key);
//校验
if (StringUtils.isNotBlank(result)) {
//插进缓存
setDataToKV(key, result);
}
} else {
//睡一会再拿
Thread.sleep(100L);
result = getData(key);
}
} finally {
//释放锁
reenLock.unlock();
}
}
return result;
}
三、缓存雪崩
1、概念
缓存雪崩表示在某一时间段缓存集中失效
,导致请求全部走数据库,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据
,缓存雪崩是不同数据
都过期了,很多数据都查不到从而查数据库。
使缓存集中失效的原因:
1)、redis服务器挂掉了。
Redis 集群产生了大面积故障;缓存失败,此时仍有大量请求去访问 Redis缓存服务器;在大量 Redis 请求失败后,这些请求将会去访问数据库;
2、对缓存数据设置了相同的过期时间,导致某时间段内缓存集中失效。
2、解决方案
1)实现Redis的高可用
【事前】搭建Redis 哨兵(Sentinel) 或 Redis 集群(Cluster) 都可以做到高可用;
【事中】缓存降级(临时支持):当访问次数急剧增加导致服务出现问题时,我们如何确保服务仍然可用。在国内使用比较多的是 Hystrix,它通过熔断、降级、限流三个手段来降低雪崩发生后的损失。只要确保数据库不死,系统总可以响应请求。
【事后】Redis备份和快速预热:Redis数据备份和恢复、快速缓存预热。
2)缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
(1)采取不同分类商品,缓存不同周期
。在同一分类中的商品,加上一个随机因子
。这样能尽可能分散缓存过期时间,而且,热门类目的商品缓存时间长一些,冷门类目的商品缓存时间短一些,也能节省缓存服务的资源。
(2)如果缓存数据库是分布式部署,将 热点数据均匀分布在不同的缓存数据库中。
(3)设置热点数据永远不过期。
四、数据库与缓存一致性
使用缓存,可以降低耗时,提供系统吞吐性能。但是,使用缓存,会存在数据一致性的问题。
1、旁路缓存模式
一般我们使用缓存,都是旁路缓存模式,它的特点就是读的时候插入缓存,写的时候删除缓存
。
1)读请求流程如下:
- 读的时候,先读缓存,缓存命中的话,直接返回数据;
- 缓存没有命中的话,就去读数据库,从数据库取出数据,放入缓存后,同时返回响应。
2)写流程如下:
这里就有两个问题思考:
1)为什么写请求要做删除库存操作,而不是做插入缓存动作?
2)为什么是先操作数据库在删除旧的缓存,能对换一下顺序吗?
2、删除缓存呢,还是更新缓存?
我们在操作缓存的时候,到底应该删除缓存还是更新缓存呢?我们先来看个例子:
- 线程A先发起一个写操作,第一步先更新数据库;
- 线程B再发起一个写操作,第二步更新了数据库;
- 由于网络等原因,线程B先更新了缓存;
- 线程A更新缓存。
这时候,缓存保存的是A的数据(老数据),数据库保存的是B的数据(新数据),数据不一致了,脏数据出现啦
。如果是删除缓存取代更新缓存则不会出现这个脏数据问题。
3、先操作数据库还是先操作缓存
双写的情况下,先操作数据库还是先操作缓存?我们再来看一个例子:假设有A、B两个请求,请求A做更新操作,请求B做查询读取操作。
- 线程A发起一个写操作,第一步del cache;
- 此时线程B发起一个读操作,cache miss;
- 线程B继续读DB,读出来一个老数据;
- 然后线程B把老数据设置入cache;
- 线程A写入DB最新的数据;
这样就有问题啦,缓存和数据库的数据不一致了。缓存保存的是老数据,数据库保存的是新数据。因此,Cache-Aside缓存模式,选择了先操作数据库而不是先操作缓存。
声明
公众号如需转载该篇文章,那麻烦在文章的头部 声明是转至公众号: 后端元宇宙。尊重作者辛苦劳动果实嘛。同时也可以问本人要该文章markdown原稿和原图片。其它情况一律禁止转载哦!
Redis缓存穿透、击穿、雪崩,数据库与缓存一致性的更多相关文章
- Redis中几个简单的概念:缓存穿透/击穿/雪崩,别再被吓唬了
Redis中几个“看似”高大上的概念,经常有人提到,某些好事者喜欢死扣概念,实战没多少,嘴巴里冒出来的全是高大上的名词,个人一向鄙视概念党,呵呵! 其实这几个概念:缓存穿透/缓存击穿/缓存雪崩,有一个 ...
- NoSQL & Redis 介绍、缓存穿透 & 击穿 & 雪崩
1. NoSql 简介 2. Redis 简介 2.1 Redis 的起源 2.2 缓存过期 & 缓存淘汰 3. 缓存异常 1)缓存穿透 2)缓存击穿 3)缓存雪崩 4)总结 1. NoSQL ...
- Redis 穿透 & 击穿 & 雪崩
原文:https://www.cnblogs.com/binghe001/p/13661381.html 缓存穿透 如果在请求数据时,在缓存层和数据库层都没有找到符合条件的数据,也就是说,在缓存层和数 ...
- Redis缓存穿透和雪崩
缓存穿透 用户想要查询一个数据 在redis缓存数据库中没有获取到 就会向后端的数据库中查询. 当用户很多 都去访问后端数据库的话,这就会给数据库带来很大的压力. 常见场景:秒杀活动 等 解决方法: ...
- 缓存穿透、雪崩、热点与Redis
(拼多多问:Redis雪崩解决办法) 导读:互联网系统中不可避免要大量用到缓存,在缓存的使用过程中,架构师需要注意哪些问题?本文以 Redis 为例,详细探讨了最关键的 3 个问题. 一.缓存穿透预防 ...
- Redis系列(八)--缓存穿透、雪崩、更新策略
1.缓存更新策略 1.LRU/LFU/FIFO算法剔除:例如maxmemory-policy 2.超时剔除,过期时间expire,对于一些用户可以容忍延时更新的数据,例如文章简介内容改了几个字 3.主 ...
- 《Redis - 穿透/击穿/雪崩/集中失效》
一:什么是缓存穿透? - 定义 - 正常情况下,我们在理想的条件下去查询缓存数据都是存在的. - 那么请求去查询一条数据库中不存在的数据,也就是缓存和数据库都查询不到这条数据. - 所以请求每次都会打 ...
- Redis-缓存穿透/击穿/雪崩
1. 简介 如图所示,一个正常的请求 客户端请求张铁牛的博客. 服务首先会请求redis,查看请求的内容是否存在. redis将请求结果返回给服务,如果返回的结果有数据则执行7:如果没有数据则会继续往 ...
- redis的穿透和雪崩
穿透: 从缓存中查询一个数据,查到为空,需要每次都去数据库中查询.而从数据库中查询出来也为空,也就不写入缓存.导致一个不存在的数每次都去数据库中查询,造成db系统很大压力 造成缓存穿透 解决:如果从数 ...
- Redis缓存穿透,缓存击穿,缓存雪崩,热点Key
导读 使用Redis难免会遇到Redis缓存穿透,缓存击穿,缓存雪崩,热点Key的问题.有些同学可能只是会用Redis来存取,基本都是用项目里封装的工具类来操作.但是作为开发,我们使用Redis时可能 ...
随机推荐
- 深入理解Redis 数据结构—简单动态字符串sds
Redis是用ANSI C语言编写的,它是一个高性能的key-value数据库,它可以作用在数据库.缓存和消息中间件.其中 Redis 键值对中的键都是 string 类型,而键值对中的值也是有 st ...
- No 'Access-Control-Allow-Origin' header: 跨域问题踩坑记录
前言 前两周在服务器上部署一个系统时,遇到了跨域问题,这也不是第一次遇到跨域问题了,本来以为解决起来会很顺利,没想到解决过程中遇到了很多坑,所以觉得有必要写一篇博客记录一下这个坑. 问题产生原因 本来 ...
- 硬盘SSD、HDD和SSHD都是什么意思?哪种类型硬盘最好?
硬盘分类:(1)HHD 机械硬盘(Mechanical hard disk)(2)SSD 固态硬盘(solid state drive/disk)(3)SSHD 混合硬盘,说白了就是HDD+SSD=S ...
- Shell脚本字符串截取方法总结
Shell脚本8种字符串截取方法总结转自:https://www.cnblogs.com/ralphdc/p/8032335.html Linux 的字符串截取很有用.有八种方法.假设有变量 var= ...
- ORACLE 查询sql和存储性能思路
1.确定session id 如果是存储过程,在程序开头设置客户端标识.并根据标识获取session id. DBMS_SESSION.set_identifier('XXX'); select * ...
- RAC常见的宏
1. RAC 作用:用来给某个对象的某个属性绑定信号,只要产生信号内容就会把内容给属性赋值 RAC(_label, text) = _textField.ra ...
- 【Python】【Module】re
python中re模块提供了正则表达式相关操作 字符: . 匹配除换行符以外的任意字符 \w 匹配字母或数字或下划线或汉字 \s 匹配任意的空白符 \d 匹配数字 \b 匹配单词的开始或结束 ^ 匹配 ...
- Spring Boot 自动扫描组件
使用@ComponentScan自动扫描组件 案例准备 1.创建一个配置类,在配置类上添加 @ComponentScan 注解.该注解默认会扫描该类所在的包下所有的配置类,相当于之前的 <con ...
- ABP.VNext-模块
一.什么是ABP.Vnext? ABP.Vnext是一个基于Asp.Net Core Web应用程序框架.主要目的是用来快速开发Web应用, ABP.Vnext不仅提供完整Web应用程序开发模板,而且 ...
- 30个类手写Spring核心原理之依赖注入功能(3)
本文节选自<Spring 5核心原理> 在之前的源码分析中我们已经了解到,依赖注入(DI)的入口是getBean()方法,前面的IoC手写部分基本流程已通.先在GPApplicationC ...