redis 查询缓存_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....
在实际的工作项目中, 缓存成为高并发、高性能架构的关键组件 ,那么Redis为什么可以作为缓存使用呢?首先可以作为缓存的两个主要特征:
- 在分层系统中处于内存/CPU具有访问性能良好,
- 缓存数据饱和,有良好的数据淘汰机制
由于Redis 天然就具有这两个特征,Redis基于内存操作的,且其具有完善的数据淘汰机制,十分适合作为缓存组件。
其中,基于内存操作,容量可以为32-96GB,且操作时间平均为100ns,操作效率高。而且数据淘汰机制众多,在Redis 4.0 后就有8种了促使Redis作为缓存可以适用很多场景。
那Redis缓存为什么需要数据淘汰机制呢?有哪8种数据淘汰机制呢?
数据淘汰机制
Redis缓存基于内存实现的,则其缓存其容量是有限的,当出现缓存被写满的情况,那么这时Redis该如何处理呢?
Redis对于缓存被写满的情况,Redis就需要缓存数据淘汰机制,通过一定淘汰规则将一些数据刷选出来删除,让缓存服务可再使用。那么Redis使用哪些淘汰策略进行刷选删除数据?
在Redis 4.0 之后,Redis 缓存淘汰策略6+2种,包括分成三大类:
- 不淘汰数据
- noeviction ,不进行数据淘汰,当缓存被写满后,Redis不提供服务直接返回错误。
- 在设置过期时间的键值对中,
- volatile-random ,在设置过期时间的键值对中随机删除
- volatile-ttl ,在设置过期时间的键值对,基于过期时间的先后进行删除,越早过期的越先被删除。
- volatile-lru , 基于LRU(Least Recently Used) 算法筛选设置了过期时间的键值对, 最近最少使用的原则来筛选数据
- volatile-lfu ,使用 LFU( Least Frequently Used ) 算法选择设置了过期时间的键值对, 使用频率最少的键值对,来筛选数据。
- 在所有的键值对中,
- allkeys-random, 从所有键值对中随机选择并删除数据
- allkeys-lru, 使用 LRU 算法在所有数据中进行筛选
- allkeys-lfu, 使用 LFU 算法在所有数据中进行筛选
img
Note: LRU( 最近最少使用,Least Recently Used)算法, LRU维护一个双向链表 ,链表的头和尾分别表示 MRU 端和 LRU 端,分别代表最近最常使用的数据和最近最不常用的数据。
LRU 算法在实际实现时,需要用链表管理所有的缓存数据,这会带来额外的空间开销。而且,当有数据被访问时,需要在链表上把该数据移动到 MRU 端,如果有大量数据被访问,就会带来很多链表移动操作,会很耗时,进而会降低 Redis 缓存性能。
其中,LRU和LFU 基于Redis的对象结构redisObject的lru和refcount属性实现的:
typedef struct redisObject { unsigned type:4; unsigned encoding:4; // 对象最后一次被访问的时间 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency // 引用计数 * and most significant 16 bits access time). */ int refcount; void *ptr; } robj; 复制代码
Redis的LRU会使用redisObject的lru记录最近一次被访问的时间,随机选取参数maxmemory-samples 配置的数量作为候选集合,在其中选择 lru 属性值最小的数据淘汰出去。
在实际项目中,那么该如何选择数据淘汰机制呢?
- 优先选择 allkeys-lru算法,将最近最常访问的数据留在缓存中,提升应用的访问性能。
- 有顶置数据使用 volatile-lru算法 ,顶置数据不设置缓存过期时间,其他数据设置过期时间,基于LRU 规则进行筛选 。
在理解了Redis缓存淘汰机制后,来看看Redis作为缓存其有多少种模式呢?
Redis缓存模式
Redis缓存模式基于是否接收写请求,可以分成只读缓存和读写缓存:
只读缓存:只处理读操作,所有的更新操作都在数据库中,这样数据不会有丢失的风险。
- Cache Aside模式
image.png
读写缓存,读写操作都在缓存中执行,出现宕机故障,会导致数据丢失。缓存回写数据到数据库有分成两种同步和异步:
- 同步:访问性能偏低,其更加侧重于保证数据可靠性
- Read-Throug模式
- Write-Through模式
- 异步:有数据丢失风险,其侧重于提供低延迟访问
- Write-Behind模式
image.png
Cache Aside模式
查询数据先从缓存读取数据,如果缓存中不存在,则再到数据库中读取数据,获取到数据之后更新到缓存Cache中,但更新数据操作,会先去更新数据库种的数据,然后将缓存种的数据失效。
而且Cache Aside模式会存在并发风险:执行读操作未命中缓存,然后查询数据库中取数据,数据已经查询到还没放入缓存,同时一个更新写操作让缓存失效,然后读操作再把查询到数据加载缓存,导致缓存的脏数据。
Read/Write-Throug模式
查询数据和更新数据都直接访问缓存服务,缓存服务同步方式地将数据更新到数据库。出现脏数据的概率较低,但是就强依赖缓存,对缓存服务的稳定性有较大要求,但同步更新会导致其性能不好。
Write Behind模式
查询数据和更新数据都直接访问缓存服务,但缓存服务使用异步方式地将数据更新到数据库(通过异步任务) 速度快,效率会非常高,但是数据的一致性比较差,还可能会有数据的丢失情况,实现逻辑也较为复杂。
在实际项目开发中根据实际的业务场景需求来进行选择缓存模式。那了解上述后,我们的应用中为什么需要使用到redis缓存呢?
在应用使用Redis缓存可以提高系统性能和并发,主要体现在
- 高性能:基于内存查询,KV结构,简单逻辑运算
- 高并发: Mysql 每秒只能支持2000左右的请求,Redis轻松每秒1W以上。让80%以上查询走缓存,20%以下查询走数据库,能让系统吞吐量有很大的提高
虽然使用Redis缓存可以大大提升系统的性能,但是使用了缓存,会出现一些问题,比如,缓存与数据库双向不一致、缓存雪崩等,对于出现的这些问题该怎么解决呢?
使用缓存常见的问题
使用了缓存,会出现一些问题,主要体现在:
- 缓存与数据库双写不一致
- 缓存雪崩: Redis 缓存无法处理大量的应用请求,转移到数据库层导致数据库层的压力激增;
- 缓存穿透:访问数据不存在在Redis缓存中和数据库中,导致大量访问穿透缓存直接转移到数据库导致数据库层的压力激增;
- 缓存击穿:缓存无法处理高频热点数据,导致直接高频访问数据库导致数据库层的压力激增;
缓存与数据库数据不一致
只读缓存(Cache Aside模式)
对于只读缓存(Cache Aside模式), 读操作都发生在缓存中,数据不一致只会发生在删改操作上(新增操作不会,因为新增只会在数据库处理),当发生删改操作时,缓存将数据中标志为无效和更新数据库 。因此在更新数据库和删除缓存值的过程中,无论这两个操作的执行顺序谁先谁后,只要有一个操作失败了就会出现数据不一致的情况。
总结出,当不存在并发的情况使用重试机制(消息队列使用),当存在高并发的情况,使用延迟双删除(在第一次删除后,睡眠一定时间后,再进行删除),具体如下:
操作顺序 | 是否高并发 | 潜在问题 | 现象 | 应对方案 |
---|
NOTE:
延迟双删除伪代码:
redis.delKey(X) db.update(X) Thread.sleep(N) redis.delKey(X)
读写缓存(Read/Write-Throug、Write Behind模式 )
对于读写缓存,写操作都发生在缓存中,后再更新数据库,只要有一个操作失败了就会出现数据不一致的情况。
总结出,当不存在并发的情况使用重试机制(消息队列使用),当存在高并发的情况,使用分布锁。具体如下:
操作顺序 | 是否高 并发 | 潜在问题 | 现象 | 应对方案 |
---|
缓存雪崩
缓存雪崩,由于缓存中有大量数据同时过期失效或者缓存出现宕机,大量的应用请求无法在 Redis 缓存中进行处理,进而发送到数据库层导致数据库层的压力激增,严重的会造成数据库宕机。
对于缓存中有大量数据同时过期,导致大量请求无法得到处理, 解决方式:
- 数据预热,将发生大并发访问前手动触发加载缓存不同的key, 可以避免在用户请求的时候,先查询数据库
- 设置不同的过期时间,让缓存失效的时间点尽量均匀
- 双层缓存策略, 在原始缓存上加上拷贝缓存,原始缓存失效时可以访问拷贝缓存,且原始缓存失效时间设置为短期,拷贝缓存设置为长期
- 服务降级 , 发生缓存雪崩时,针对不同的数据采取不同的降级方案 ,比如,非核心数据直接返回预定义信息、空值或是错误信息
对于缓存出现宕机,解决方式:
- 业务系统中实现服务熔断或请求限流机制,防止大量访问导致数据库出现宕机
缓存穿透
缓存穿透,数据在数据库和缓存中都不存在,这样就导致查询数据,在缓存中找不到对应key的value,都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。
当有大量访问请求,且其绕过缓存直接查数据库,导致数据库层的压力激增,严重的会造成数据库宕机。
对于缓存穿透,解决方式:
- 缓存空值或缺省值,当一个查询返回的数据为空时, 空结果也将进行缓存,并将它的过期时间设置比较短,下次访问直接从缓存中取值,避免了把大量请求发送给数据库处理,造成数据库出问题。
- 布隆过滤器( BloomFilter ),将所有可能查询数据key哈希到一个足够大的bitmap中 , 在查询的时候先去BloomFilter去查询key是否存在,如果不存在就直接返回,存在再去查询缓存,缓存中没有再去查询数据库 ,从而避免了数据库层的压力激增出现宕机。
缓存击穿
缓存击穿,针对某个访问非常频繁的热点数据过期失效,导致访问无法在缓存中进行处理,进而会有导致大量的直接请求数据库,从而使得数据库层的压力激增,严重的会造成数据库宕机。
对于缓存击穿,解决方式:
- 不设置过期时间,对于访问特别频繁的热点数据,不设置过期时间。
总结
在大多数业务场景下,Redis缓存作为只读缓存使用。针对只读缓存来说, 优先使用先更新数据库再删除缓存的方法保证数据一致性 。
其中,缓存雪崩,缓存穿透,缓存击穿三大问题的原因和解决方式
作者:Ccww
链接:https://juejin.cn/post/6919271744754679815
redis 查询缓存_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....相关推荐
- Redis的过期策略以及内存淘汰机制
Redis的过期策略以及内存淘汰机制 我们知道,redis中缓存的数据是有过期时间的,当缓存数据失效时,redis会删除过期数据以节省内存,那redis是怎样删除过期数据的?删除过期数据的策略是什么? ...
- Redis系列(四)--内存淘汰机制(含单机版内存优化建议)
每台redis的服务器的内存都是有限的,而且也不是所有的内存都用来存储信息.而且redis的实现并没有在内存这块做太多的优化,所以实现者为了防止内存过于饱和,采取了一些措施来管控内存. 文章结构: ( ...
- Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!...
点击上方关注 "终端研发部" 设为"星标",和你一起掌握更多数据库知识 于哥你好,最近面试挺多的,尤其是在问到java面试题,Redis被问的特别多,比如Red ...
- 一文通透讲解Redis高级特性,多线程/持久化/淘汰机制等统统搞定
Redis 是一个开源的,基于内存的可持久化的非关系型数据库存储系统.在实际项目中可以用 Redis 做缓存或消息服务器,Redis 也是目前互联网中使用比较广泛的非关系型数据库,下面就来深入分析Re ...
- java redis 主从 哨兵_Redis主从复制与哨兵机制
Redis主从复制 1.redis的复制功能是支持多个数据库之间的数据同步.一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数 ...
- mysql缓存淘汰机制_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....
在实际的工作项目中, 缓存成为高并发.高性能架构的关键组件 ,那么Redis为什么可以作为缓存使用呢?首先可以作为缓存的两个主要特征: 在分层系统中处于内存/CPU具有访问性能良好, 缓存数据饱和,有 ...
- mysql缓存淘汰机制_聊聊缓存淘汰算法-LRU 实现原理
前言 我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来.缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据.常用淘 ...
- Redis进阶 - 缓存问题:一致性, 穿击, 穿透, 雪崩, 污染等
Redis最常用的一个场景就是作为缓存,本文主要探讨Redis作为缓存,在实践中可能会有哪些问题?比如一致性, 穿击, 穿透, 雪崩, 污染等. 为什么要理解Redis缓存问题 在高并发的业务场景下, ...
- onbeforeedit和onbeginedit数据不一致_Redis缓存与数据库产生不一致的问题该如何解决?...
不一致产生的原因 我们在使用redis过程中,通常会这样做:先读取缓存,如果缓存不存在,则读取数据库.伪代码如下: Object stuObj = new Object();public Stu ge ...
最新文章
- linux sftp 中文,Linux(CentOS)上配置 SFTP服务器
- 通过结束进程来关闭程序
- 关于async与await的FAQ 转
- python if写在return 后面_python中return如何写
- predis操作大全
- 堆排序c语言6,C语言:十种排序(七) - 堆排序
- spark学习11(Wordcount程序-本地测试)
- 通达oa 2013 php解密,通达OA 2011-2013 通杀GETSHELL修复补丁
- Android字体设置,Roboto字体使用
- 阿里巴巴矢量图标 iconfont 下载图标分辨率小一点、并占得内存小一点呢
- PDF文件怎么制作,PDF文件制作方法
- 深度deepin安装腾达U12无线网卡驱动
- android音乐播放器歌词解析,iOS 音乐播放器歌词解析
- HTML|内联CSS-背景和字体
- matlab函数表达式里分号_matlab中分号、冒号、逗号等常用标点符号的功能和用法总结...
- .prevent 与 .stop,以及解决其他地方长按,文本被选中的问题
- linux命令忽略大小写zip,Linux基础命令---unzip
- 咸鱼成长故事简介yyds
- 并注册烧写钩子 获取启动介质类型_一种基于USB烧写的数据传输方法与流程
- 【超详细】(蓝桥杯Python)阶乘约数:求100阶乘的正约数个数。
热门文章
- ASP.NET Core使用Jaeger实现分布式追踪
- Wexflow:C#中的开源工作流引擎
- 有效事件: 可取代数十种设计模式
- 使用 C# 运行符号测试
- 新春大吉,2017 Make .NET Great
- ExceptionLess新玩法 — 记日志
- ASP.NET Core开发-Docker部署运行
- 升讯威微信营销系统开发实践:(2)功能设计与架构设计
- mysql数据库项目化教程郑小蓉_MySQL数据库项目化教程(高等职业教育“十三五”规划教材(软件技术专业))...
- matlab 如何hidden,Matlab基本函数-hidden函数