目录
- 1.缓存穿透
- 解决方案一:缓存空数据
- 解决方案二:布隆过滤器
- 2.缓存击穿
- 解决方案一:互斥锁
- 解决方案二:设置当前key逻辑过期
- 3.缓存雪崩
- 1.给不同的Key的TTL添加随机值
- 2.利用Redis集群提高服务的可用性
- 3.给缓存业务添加降级限流策略
- 4.给业务添加多级缓存
- 4.双写一致性
- 1.问题:redis做为缓存,mysql的数据如何与redis进行同步呢?
- 2.异步通知保证数据的最终一致性
- 5.Redis持久化的方式
- 1.RDB
- RDB的执行原理
- 2.AOF
- 3.RDB与AOF对比
- 6.Redis数据过期策略
- 1.惰性删除
- 2.定期删除
- 7.数据淘汰策略
- 使用建议:
1.缓存穿透
①缓存穿透是指在使用缓存系统时,
频繁请求一个不存在于缓存中的数据
,导致缓存系统无法起到预期的加速作用,而直接请求数据库或其他底层存储系统。
②缓存系统一般通过将数据存储在内存中,以提高读取速度。当一个请求到达时,缓存系统会先检查是否有缓存数据,如果有则直接返回,如果没有则从底层存储系统中读取数据,并将其缓存起来以备后续使用。
③然而,如果频繁请求一个不存在于缓存中的数据,每次请求都会直接访问底层存储系统,无法从缓存中获取数据,导致缓存系统无法发挥作用。这种情况下,即使缓存系统存在,仍然会
对底层存储系统产生很大的负载
,甚至可能导致底层存储系统崩溃。
④缓存穿透可能会发生的原因包括
恶意攻击、大量的并发请求和数据更新
等。为了解决缓存穿透的问题,可以采取一些修复措施,例如使用布隆过滤器
来过滤掉不存在的数据请求、设置缓存的过期时间
、对缓存进行预热
等。
查询一个
不存在的数据
,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库
解决方案一:缓存空数据
缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存。
①优点
:简单
②缺点
:消耗内存,可能会发生不一致的问题
解决方案二:布隆过滤器
布隆过滤器(
Bloom Filter
)是一种用于判断某个元素是否存在于集合中的数据结构
,它的特点是高效地判断一个元素是否存在于集合中,并且占用的内存空间相对较小。
布隆过滤器的核心是一个
位数组(bit array)
和一组哈希函数
。当一个元素被加入布隆过滤器时,通过哈希函数将其映射为多个位数组的索引位置,并将这些位置的值设置为1。当判断一个元素是否存在于布隆过滤器时,同样通过哈希函数将其映射为多个位数组的索引位置,并检查这些位置的值是否都为1。如果有任何一个位置的值为0,则可以确定该元素不存在于集合中;如果所有位置的值都为1,则表示该元素可能存在于集合中,但不能确定是否真的存在,可能会存在误判
的情况。
布隆过滤器的优点是占用的
内存空间相对较小,且判断的速度非常快
。但它也有一些缺点,其中最主要的就是可能存在误判的情况。由于布隆过滤器使用了多个哈希函数,所以在判断元素是否存在时可能会产生哈希冲突,导致误判。另外,布隆过滤器无法删除已经加入的元素
,因为删除一个元素可能会影响到其他元素的判断结果。
布隆过滤器在实际应用中常用于缓存、数据库查询等领域,可以有效地提高查询效率和减轻底层存储系统的负载
。
布隆过滤器作用
:布隆过滤器可以用于检索一个元素是否在一个集合中。
bitmap(位图)
︰相当于是一个以(bit)位
为单位的数组,数组中每个单元只能存储二进制数0或1
误判率
:数组越小误判率就越大,数组越大误判率就越小,但是同时带来了更多的内存消耗。
①
优点
:内存占用较少,没有多余key
②缺点
:实现复杂,存在误判
2.缓存击穿
缓存击穿
:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求
过来,这些并发的请求可能会瞬间把DB压垮.
解决方案一:互斥锁
- 强一致
- 性能差
当缓存失效时,不立即去load db,先使用如Redis的
setnx
去设置一个互斥锁,当操作成功返回时再进行load db的操作并回设缓存,否则重试get缓存的方法
解决方案二:设置当前key逻辑过期
- 高可用,性能优
- 不能保证数据绝对一致
①在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间
②当查询的时候,从redis取出数据后判断时间是否过期
③如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新
3.缓存雪崩
缓存雪崩是指
在同一时段大量的缓存key同时失效或者Redis服务宕机
,导致大量请求到达数据库,带来巨大压力。
1.给不同的Key的TTL添加随机值
合理设置缓存失效时间:根据业务场景和数据特点设置合理的缓存失效时间,避免大量数据在同一时间失效。可以根据数据的访问频率和重要性来
设置不同的失效时间
。
解决方案主要是可以将缓存失效时间分散开,比如可以
在原有的失效时间基础上增加一个随机值
,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件
2.利用Redis集群提高服务的可用性
哨兵模式、集群模式
-
数据分片
:Redis 集群可以将数据按照一定的规则分片存储在不同的节点上,以提高数据处理能力和并发访问能力。 -
主从复制
:通过配置 Redis 主从复制,可以将主节点的数据同步到多个从节点,实现数据的备份和故障恢复。当主节点发生故障时,从节点可以自动切换为主节点,保证服务的可用性。 -
Sentinel 哨兵机制
:Redis Sentinel 是 Redis 自带的高可用性解决方案,可以监控主节点和从节点的状态,并在主节点故障时自动选举新的主节点。哨兵还可以监控和修复其他节点的故障。 -
高可用性架构
:搭建 Redis 集群时可以采用一主多从的高可用性架构,通过增加主节点和从节点来提高服务的可用性和并发处理能力。 -
集群模式
:Redis 3.0 版本引入了 Cluster 集群模式,可以实现数据分片和自动故障转移。利用 Redis Cluster 可以搭建分布式的 Redis 集群,提高数据存储和处理的能力。 -
客户端链接失败重试
:在客户端访问 Redis 集群时,可以设置失败重试机制,当某个节点连接失败时,自动尝试连接其他可用节点,提高服务的可用性。
3.给缓存业务添加降级限流策略
ngxin或spring cloud gateway
降级可做为系统的保底策略,适用于
穿透、击穿、雪崩
限流降级
:在高并发情况
下,适当地对请求进行限流或降级,保护后端服务器的稳定性。
4.给业务添加多级缓存
Guava或Caffeine
使用多级缓存:将缓存分为多个层级,比如本地缓存和分布式缓存。
本地缓存可以使用内存,而分布式缓存可以使用 Redis。
这样即使 Redis 出现问题,本地缓存仍然可以提供部分服务。
4.双写一致性
1.问题:redis做为缓存,mysql的数据如何与redis进行同步呢?
可以通过以下几种方式将MySQL的数据同步到Redis缓存中:
-
手动同步:当MySQL中的数据发生变化时,手动编写代码将这些变化同步到Redis中。这可以通过使用MySQL的
触发器
或在代码中监听数据变化事件来实现。在触发器或事件中,将相应的数据插入、更新或删除到Redis中。 -
定时同步:定时将MySQL中的数据同步到Redis中。可以使用定时任务工具(例如Cron)来实现。设置一个定时任务,定期查询MySQL中的数据,然后将查询结果同步到Redis中。
-
增量同步
:记录MySQL中数据的变化,只将发生变化的数据同步到Redis中。可以通过使用MySQL的二进制日志(binlog)
来捕获MySQL中所有的修改操作,然后解析binlog,将修改的数据同步到Redis中。 -
使用消息队列
:将MySQL中的数据变化通过消息队列传递给Redis,然后Redis消费这些消息来同步数据。可以使用消息队列工具(如RabbitMQ或Kafka
)来实现。当MySQL中的数据发生变化时,将变化的数据作为消息发布到消息队列中,然后Redis作为消费者从消息队列中获取消息并将数据同步到Redis中。
无论使用哪种方式进行同步,都需要注意数据的一致性和并发性
。在同步过程中,需要考虑数据的读写锁、事务处理和冲突解决等问题,以确保数据在MySQL和Redis之间的一致性。
双写一致性
:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致
- 读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间
- 写操作:
延迟双删
共享锁
:读锁readLock,加锁之后,其他线程可以共享读操作
排他锁
:也叫独占锁writeLock,加锁之后,阻塞其他线程读写操作
2.异步通知保证数据的最终一致性
使用MQ中间中间件,更新数据之后,通知缓存删除
利用canal
中间件,不需要修改业务代码,伪装为mysql的一个从节点,canal通过读取binlog数据更新缓存
二进制日志(
BINLOG
)记录了所有的DDL(数据定义语言)语句和DML (数据操纵语言)语句
但不包括数据查询(SELECT、SHOW)语句。
5.Redis持久化的方式
1.RDB
RDB全称
Redis Database Backup
file (Redis数据备份文件),也被叫做Redis数据快照
。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据
Redis内部有触发RDB的机制,可以在redis.conf文件中找到.
RDB的执行原理
bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据
完成fork后读取内存数据并写入RDB文件。
fork采用的是copy-on-write
技术:
- 当主进程执行读操作时,访问共享内存;
- 当主进程执行写操作时,则会拷贝一份数据,执行写操作。
2.AOF
AOF全称为
Append Only File
(追加文件)。
Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件
。
AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF。
AOF的命令记录的频率也可以通过redis.conf文件来配:
因为是记录命令,
AOF文件会比RDB文件大的多
。
而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。
通过执行bgrewriteaof
命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。
Redis也会
在触发阈值时自动去重写AOF文件
。阈值也可以在redis.conf中配置
3.RDB与AOF对比
RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。
6.Redis数据过期策略
Redis对数据设置数据的有效时间,数据过期以后,就需要将数据从内存中删除掉。可以按照不同的规则进行删除,这种删除规则就被称之为数据的删除策略(
数据过期策略
)。
1.惰性删除
设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,
如果过期,我们就删掉它,反之返回该key
.
- 优点∶对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
- 缺点∶对内存不友好,如果一个key已经过期,但是一直没有使用,那么
该key就会一直存在内存中
,内存永远不会释放
2.定期删除
每隔一段时间
,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key
)。
定期清理有两种模式:
SLOW模式
是定时任务,执行频率默认为10hz,每次不超过25ms
,以通过修改配置文件redis.conf的hz选项
来调整这个次数FAST模式
执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms
①
优点
:可以通过限制删除操作执行的时长和频率
来减少删除操作对CPU的影响。另外定期删除,也能有效释放过期键占用的内存。
②缺点
:难以确定删除操作执行的时长和频率。
Redis的过期删除策略:惰性删除+定期删除
两种策略进行配合使用
7.数据淘汰策略
当Redis中的
内存不够用时
,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉
,这种数据的删除规则被称之为内存的淘汰策略
。
①LRU (
Least Recently Used
)最近最少使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
②LFU (Least Frequently Used
)最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。
Redis支持8种不同策略来选择要删除的key:
noeviction
:不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略
volatile-ttl
:对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰allkeys-random
: 对全体key,随机进行淘汰volatile-random
:对设置了TTL的key,随机进行淘汰allkeys-Iru
:对全体key,基于LRU算法进行淘汰volatile-lru
:对设置了TTL的key,基于LRU算法进行淘汰allkeys-lfu
: 对全体key,基于LFU算法进行淘汰volatile-lfu
:对设置了TTL的key,基于LFU算法进行淘汰
使用建议:
1.
优先使用 alkeys-lru 策略
。充分利用LRU算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分
,建议使用。
2.如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用allkeys-random
,随机选择淘汰。
3.如果业务中有置顶的需求,可以使用volatile-lru策略
,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。
4.如果业务中有短时高频访问的数据,可以使用allkeys-lfu或 volatile-lfu策略
。