Redis 缓存常见问题
本文为博主原创,未经允许不得转载:
目录:
1. 缓存穿透
1.1 出现原因
1.2 解决方案
1.3 布隆过滤器
2. 缓存雪崩
3. 缓存失效(缓存击穿,热点缓存)
1. 缓存穿透:
缓存穿透是指查询一个根本不存在的数据, 缓存层和存储层都不会命中,如果从存储层查不到数据则不写入缓存层。将会导致缓存失去作用,也就失去了缓存提高性能和保护后端服务的作用了。如果大量的类似请求过来,将会占用数据库连接资源,使得服务性能下降。
1.1 出现原因:
1. 大量恶意请求
2. 自身业务代码或者数据出现问题
1.2 解决方案:
1. 对缓存不存在的key,存储一个空对象,并设置过期时间
String get(String key) {
// 从缓存中获取数据
String cacheValue = cache.get(key);
// 缓存为空
if (StringUtils.isBlank(cacheValue)) {
// 从存储中获取
String storageValue = storage.get(key);
cache.set(key, storageValue);
// 如果存储数据为空, 需要设置一个过期时间(300秒)
if (storageValue == null) {
cache.expire(key, 60 * 5);
}
return storageValue;
} else {
// 缓存非空
return cacheValue;
}
}
2. 使用布隆过滤器:
布隆过滤器就是一个大型的位数组和几个不一样的无偏 hash 函数。所谓无偏就是能够把元素的 hash 值算得比较均匀。向布隆过滤器中添加 key 时,会使用多个 hash 函数对 key 进行 hash 算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就完成了 add 操作。
向布隆过滤器询问 key 是否存在时,跟 add 一样,也会把 hash 的几个位置都算出来,看看位数组中这几个位置是否都为 1,只要有一个位为 0,那么说明布隆过滤器中这个key 不存在。如果都是 1,这并不能说明这个 key 就一定存在,只是极有可能存在,因为这些位被置为 1 可能是因为其它的 key 存在所致。如果这个位数组比较稀疏,这个概率就会很大,如果这个位数组比较拥挤,这个概率就会降低。
这种方法适用于数据命中不高、 数据相对固定、 实时性低(通常是数据集较大) 的应用场景, 代码维护较为复杂, 但是缓存空间占用很少。
使用 redission 的布隆过滤器:
引入依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.6.5</version>
</dependency>
示例代码:
package com.redisson; import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config; public class RedissonBloomFilter { public static void main(String[] args) {
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
//构造Redisson
RedissonClient redisson = Redisson.create(config); RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");
//初始化布隆过滤器:预计元素为100000000L,误差率为3%,根据这两个参数会计算出底层的bit数组大小
bloomFilter.tryInit(100000000L,0.03);
//将zhuge插入到布隆过滤器中
bloomFilter.add("test2"); //判断下面号码是否在布隆过滤器中
System.out.println(bloomFilter.contains("test1"));//false
System.out.println(bloomFilter.contains("test2"));//false
System.out.println(bloomFilter.contains("test3"));//true
}
}
2. 缓存雪崩
缓存雪崩指的是缓存层支撑不住或宕掉后,请求会到后端数据库层。由于缓存层承载着大量请求, 有效地保护了存储层, 但是如果缓存层由于某些原因不能提供服务(比如超大并发过来,缓存层支撑不住,或者由于缓存设计不好,类似大量请求访bigkey,导致缓存能支撑的并发急剧下降), 于是大量请求都会到存储层, 存储层的调用量会暴增, 造成数据库层也会级联宕机的情况。
解决方案:
- 事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
- 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
- 事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
3.缓存击穿
缓存击穿,就是说某个 key 非常热点,突然之间访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
不同场景下的解决方式可如下:
- 若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。
- 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 Redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
- 若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。
第二种场景伪代码:
String get(String key) {
// 从Redis中获取数据
String value = redis.get(key);
// 如果value为空, 则开始重构缓存
if (value == null) {
// 只允许一个线程重建缓存, 使用nx, 并设置过期时间ex
String mutexKey = "mutext:key:" + key;
if (redis.set(mutexKey, "1", "ex 180", "nx")) {
// 从数据源获取数据
value = db.get(key);
// 回写Redis, 并设置过期时间
redis.setex(key, timeout, value);
// 删除key_mutex
redis.delete(mutexKey);
}// 其他线程休息50毫秒后重试
else {
Thread.sleep(50);
get(key);
}
}
return value;
}
Redis 缓存常见问题的更多相关文章
- Azure Redis 缓存使用注意事项与排查问题文档整理
StackExchange.Redis 使用名为 synctimeout 的配置设置进行同步操作,该设置的默认值为 1000 毫秒. 如果同步调用未在规定时间内完成,StackExchange.Red ...
- Redis缓存设计及常见问题
Redis缓存设计及常见问题 缓存能够有效地加速应用的读写速度,同时也可以降低后端负载,对日常应用的开发至关重要.下面会介绍缓存使 用技巧和设计方案,包含如下内容:缓存的收益和成本分析.缓存更新策略的 ...
- Redis缓存策略设计及常见问题
Redis缓存设计及常见问题 缓存能够有效地加速应用的读写速度,同时也可以降低后端负载,对日常应用的开发至关重要.下面会介绍缓存使用技巧和设计方案,包含如下内容:缓存的收益和成本分析.缓存更新策略的选 ...
- redis缓存介绍以及常见问题浅析
# 没缓存的日子: 对于web来说,是用户量和访问量支持项目技术的更迭和前进.随着服务用户提升.可能会出现一下的一些状况: 页面并发量和访问量并不多,mysql足以支撑自己逻辑业务的发展.那么其实可以 ...
- Redis 面试常见问题———缓存雪崩、缓存击穿以及缓存穿透
在开发中会面临缓存异常可能会出现三个问题,分别是缓存雪崩.缓存击穿和缓存穿透.这三个问题会导致大量请求从缓存转移到数据库,如果请求的并发量很大的话,就会导致数据库崩溃.所以在面试官也会经常问这些问题. ...
- Redis缓存你必须了解的!
不管你是从事Python.Java.Go.PHP.Ruby等等… Redis都应该是一个比较熟悉的中间件.而大部分经常写业务代码的程序员,实际工作中或许只用到了set value.get value两 ...
- 缓存工厂之Redis缓存
这几天没有按照计划分享技术博文,主要是去医院了,这里一想到在医院经历的种种,我真的有话要说:医院里的医务人员曾经被吹捧为美丽+和蔼+可亲的天使,在经受5天左右相互接触后不得不让感慨:遇见的有些人员在挂 ...
- Windows下Redis缓存服务器的使用 .NET StackExchange.Redis Redis Desktop Manager
Redis缓存服务器是一款key/value数据库,读110000次/s,写81000次/s,因为是内存操作所以速度飞快,常见用法是存用户token.短信验证码等 官网显示Redis本身并没有Wind ...
- 总结:如何使用redis缓存加索引处理数据库百万级并发
前言:事先说明:在实际应用中这种做法设计需要各位读者自己设计,本文只提供一种思想.准备工作:安装后本地数redis服务器,使用mysql数据库,事先插入1000万条数据,可以参考我之前的文章插入数据, ...
- .NET基于Redis缓存实现单点登录SSO的解决方案[转]
一.基本概念 最近公司的多个业务系统要统一整合使用同一个登录,这就是我们耳熟能详的单点登录,现在就NET基于Redis缓存实现单点登录做一个简单的分享. 单点登录(Single Sign On),简称 ...
随机推荐
- TensorFlow C++ 初始化 Tensor 内存 到GPU 内存
最近使用TensorFlow C++版本实现神经网络的部署,我通过GPU 处理得到网络的输入值,因此输入值在GPU内存上保存, TF 输入tensor 的调用语句为 Tensor inputTenso ...
- 使用代码生成工具快速开发应用-结合后端Web API提供接口和前端页面快速生成,实现通用的业务编码规则管理
在前面随笔<在Winform应用中增加通用的业务编码规则生成>,我介绍了基于Winform和WPF的一个通用的业务编码规则的管理功能,本篇随笔介绍基于后端Web API接口,实现快速的Vu ...
- 【内核】基于 LSM 框架的 ELF 校验控制
欲实现操作系统对正在加载的 ELF 文件的校验控制,需要借助 LSM 框架. LSM 框架介绍 LSM 全称 Linux Security MOdule,是 Linux 的一个安全模块框架.LSM 为 ...
- jclasslib的安装
双击安装包打开后点击下一步,然后选择安装的路径. 默认路径,如果c盘有空间不建议更改路径,然后再次点击下一步 依然选择下一步 最后点击完成即可完成安装.现在可以使用啦 注:如果需要安装包,请点击下面链 ...
- JavaFx 打包jar(六)
JavaFx 打包jar(六) JavaFX 从入门入门到入土系列 我们编写了不少javafx,那么如何打包成jar给用户呢?下面我给出比较全的打包方式. 打包jar 下面我给出比较全的打包方式. 1 ...
- 文心一言 VS 讯飞星火 VS chatgpt (42)-- 算法导论5.4 6题
六.假设将n个球投人 n 个箱子里,其中每次投球独立,并且每个球等可能落入任何箱子.空箱子的数目期望是多少?正好有一个球的箱子的数目期望是多少? 文心一言: 这是一个典型的概率问题.首先,我们可以通过 ...
- 昇腾CANN 7.0 黑科技:大模型推理部署技术解密
本文分享自华为云社区<昇腾CANN 7.0 黑科技:大模型推理部署技术解密>,作者:昇腾CANN. 近期,随着生成式AI.大模型进入公众视野,越来越多的人意识到抓住AI的爆发就是抓住未来智 ...
- 如何强制SQL走性能更优的hash join
本文分享自华为云社区<[SQL优化]为什么有时候无法走执行性能更优的hashjoin>,作者: leapdb. 1. hash join通常优于nestloop join 通常nestlo ...
- 云小课|创建DDS只读节点,轻松应对业务高峰
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:为了扩展主节点的读 ...
- AppCube视角浅析: 艾瑞咨询《2022年中国低代码行业研究报告》
摘要:近日,艾瑞咨询发布了<2022年中国低代码行业研究报告>,报告从企业数字化发展背景.低代码的发展路径.应用渗透.市场规模等方面进行深入研究分析,并洞察了发展趋势. 本文分享自华为云社 ...