分布式专题(十三)Redis热点问题

本文深入探讨Redis的过期删除原理、发布订阅功能、持久化机制、内存回收策略等核心技术,同时讲解如何解决缓存与数据一致性、缓存雪崩及穿透等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、说说过期删除的原理

消极方法(passive way)

积极方法(active way)

二、说说Redis的发布订阅

三、Redis的数据是如何持久化的?

(1)RDB和AOF机制

(2)RDB的优缺点

(3)AOF机制的优缺点

(4)RDB和AOF该如何选择?

四、Redis内存回收策略

五、Redis是单进程单线程?性能为什么这么快

多路复用

六、如何解决Redis缓存与数据一致性问题?

七、如何解决缓存雪崩问题? 

缓存雪崩

解决方式

八、如何解决缓存穿透问题?

缓存穿透

解决方式


一、说说过期删除的原理

在Redis中提供了Expire命令设置一个键的过期时间,到期以后Redis会自动删除它,这个在实际使用过程中用的非常多。

EXPIRE命令的使用方法为

EXPIRE key seconds

Redis中的主键失效是如何实现的,即失效的主键是如何删除的?实际上,Redis删除失效主键的方法主要有两种:

消极方法(passive way)

在主键被访问时如果发现它已经失效,那么就删除它

积极方法(active way)

周期性地从设置了失效时间的主键中选择一部分失效的主键删除

对于那些从未被查询的key,即便他们已经过期,被动方式也无法清楚。

因此Redis会周期性地随机测试一些key, 已过期的key将会被删掉。Redis每秒会进行10次操作,具体的流程:

  1. 随机测试20个带有timeout信息的key:
  2. 删除其中已经过期的key;
  3. 如果超过25%的key被删除,则重复执行步骤1;

二、说说Redis的发布订阅

Redis提供了发布订阅功能,可以用于消息的传输,Redis提供了一组命令可以让开发者实现“发布/订阅”模式 (publish/subscribe) . 该模式同样可以实现进程间的消息传递,它的实现原理是:

发布/订阅模式包含两种角色,分别是发布者和订阅者。订阅者可以订阅一个或多个频道,而发布者可以向指定的 频道发送消息,所有订阅此频道的订阅者都会收到该消息

发布者发布消息的命令是PUBLISH,用法是

PUBLISH channel message

比如向channel.1发一条消息:hello

PUBLISH channel.1 "hello"

这样就实现了消息的发送,该命令的返回值表示接受到这条消息的订阅者数量。因为在执行这条命令的时候还没有订阅者订阅该频道,所以返回为0,另外值得注意的是消息发送出去不会持久化,如果发送之前没有订阅者,那么后续再有订阅者订阅该频道,之前的消息就收不到了

订阅者订阅消息的命令是

SUBSCRIBE channel [channel ...]

该命令同时可以订阅多个频道,比如订阅channel.1的频道。SUBSCRIBE channel.1

执行SUBSCRIBE命令后客户端就会进入订阅状态。

三、Redis的数据是如何持久化的?

大家都知道redis的数据都是存储在内容中,那么redis实例挂了或者重启了怎么办?那是不是我们的数据就全没了?其实redis提供了持久化来解决这类问题。

(1)RDB和AOF机制

RDB持久化机制原理是对redis中的数据执行者周期性的持久化,是redis默认的持久化机制,他的默认持久化策略是如果在900S之内执行了一次数据变更操作,或者在300S内执行了10次,再或者在60s执行了10000次,那么在这个900S、300S、60S将会生成一个redis数据快照文件。

AOF持久化机制对每条redis的写入指令都会做一条日志,以append-only的模式写入一个日志文件中,在redis重启的时候会回放日志文件中的指令来重新构建数据,它默认会每秒保存一个文件。

如果同时启用了RDB和AOF的话,redis会使用AOF来恢复数据,因为AOF的数据会更加完整。

在我们实际生产使用中,可以定期把RDB和AOF做备份到云服务器来做灾难恢复,数据恢复。

(2)RDB的优缺点

RDB机制有几个优点,首先它会生成多个数据文件,每个文件都代表某个时刻redis中的数据快照,这种文件非常适合我们做冷备,可以定期把这种文件推送到云服务做存储来应对灾难恢复;其次,RDB对redis对外提供的读写服务影响比较小,可以让redis保持高性能,因为redis只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化;而且由于RDB是基于数据文件恢复的,所以说相对于AOF来说更加快速。

RDB的缺点在于2个方面,首先在redis故障的时候,RDB由于它是定期去数据快照,所以说可能会丢失一部分数据;其次RDB每次在fork子进程来生成快照文件的时候,如果数据文件特别大,可能会导致对客户端提供服务暂停数毫秒,甚至数秒。

(3)AOF机制的优缺点

AOF机制的优点:1.AOF可以更好的避免数据丢失问题,默认AOF每隔1S,通过后台线程执行一次fsync操作,它最多丢失1S的数据。2.AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能比较高,而且文件不容易受损坏,redis也提供了修复受损坏AOF文件的方式,很容易修复。3.AOF日志文件可读性比较高,可以追踪误操作记录(如flush all),来删除这个日志记录进行紧急恢复。

AOF机制的缺点:1.对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大。2.AOF开启之后,支持的写QPS会比RDB支持的写QPS低,因为默认AOF会每秒去fsync一次日志。

(4)RDB和AOF该如何选择?

其实我们应该两者一起使用的,用AOF来保证尽可能的不丢失数据,作为数据恢复的第一选择,利用RDB来做冷备,当AOF丢失或者不可用的时候来使用RDB快照来快速恢复。如果单一选择RDB的话对比AOF可能会丢失部分数据,选择AOF的换又没有RDB恢复的速度快而且AOF的备份很复杂。

四、Redis内存回收策略

Redis中提供了多种内存回收策略,当内存容量不足时,为了保证程序的运行,这时就不得不淘汰内存中的一些对象,释放这些对象占用的空间,那么选择淘汰哪些对象呢?

当内存占用过多的时候,此时会进行内存淘汰,redis提供了如下策略:

  1. noeviction:当内存不足以容纳新写入数据时,新写入数据会报错。
  2. allkeys-lru:当内存不足以容纳新写入数据时,会移除最近最少使用的key。
  3. allkeys-random:当内存不足以容纳新写入数据时,会随机移除某个key。
  4. volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
  5. volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
  6. volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。

以上几个策略最常用的应该是allkeys-lru,其实这个也要根据业务场景去选择。

五、Redis是单进程单线程?性能为什么这么快

Redis采用了一种非常简单的做法,单线程来处理来自所有客户端的开发请求,Redis把任务封闭在一个线程中从而避免了线程安全问题;redis为什么是单线程?

官方的解释是,CPU并不是Redis的瓶颈所在,Redis的瓶颈主要在机器的内存和网络的带宽。那么Redis能不能处理高并发请求呢?当然是可以的,至于怎么实现的,我们来具体了解一下。

多路复用

Redis是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以I/O操作在一般情况下往往不能直接返回,这会导致某一文件的I/O阻塞导致整个进程无法对其他客户提供服务,而I/O多路复用就是为了解决这个问题而出现的。

简单了解下几种I/O模型

  • 同步阻塞IO(Blocking IO):即传统的IO模型。
  • 同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为 NONBLOCK。
  • IO多路复用(IO Multiplexing):即经典的Reactor设计模式,也称为异步阻塞IO,Java中的Selector和 Linux中的epoll都是这种模型。
  • 异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。

同步和异步,指的是用户线程和内核的交互方式
阻塞和非阻塞,指用户线程调用内核IO操作的方式是阻塞还是非阻塞
Redis使用多路复用的方式,避免了线程的阻塞,在缓存区数据未准备好的情况下,增加了对其的事件监听。

六、如何解决Redis缓存与数据一致性问题?

对于读多写少的高并发场景,我们会经常使用缓存来进行优化。比如说支付宝的余额展示功能,实际上99%的时候都是查询,1%的请求是变更(除非是土豪,每秒钟都有收入在不断更改余额),所以,我们在这样的场景下,可以加入缓存,用户->余额

那么基于上面的这个出发点,问题就来了,当用户的余额发生变化的时候,如何更新缓存中的数据,也就是说。

  1. 我是先更新缓存中的数据再更新数据库的数据
  2. 还是修改数据库中的数据再更新缓存中的数据

这就是我们经常会在面试遇到的问题,数据库的数据和缓存中的数据如何达到一致性?首先,可以肯定的是, redis中的数据和数据库中的数据不可能保证事务性达到统一的,这个是毫无疑问的,所以在实际应用中,我们都是基于当前的场景进行权衡降低出现不一致问题的出现概率

更新缓存还是让缓存失效

更新缓存表示数据不但会写入到数据库,还会同步更新缓存; 而让缓存失效是表示只更新数据库中的数据,然后删除缓存中对应的key。那么这两种方式怎么去选择?这块有一个衡量的指标。

  1. 如果更新缓存的代价很小,那么可以先更新缓存,这个代价很小的意思是我不需要很复杂的计算去获得最新的余额数字。
  2. 如果是更新缓存的代价很大,意味着需要通过多个接口调用和数据查询才能获得最新的结果,那么可以先淘汰缓存。淘汰缓存以后后续的请求如果在缓存中找不到,自然去数据库中检索。

更新数据库和更新缓存这两个操作,是无法保证原子性的,所以我们需要根据当前业务的场景的容忍性来选择。也就是如果出现不一致的情况下,哪一种更新方式对业务的影响最小,就先执行影响最小的方案 

七、如何解决缓存雪崩问题? 

当缓存大规模渗透在整个架构中以后,那么缓存本身的可用性讲决定整个架构的稳定性。那么接下来我们来讨论下缓存在应用过程中可能会导致的问题。 

缓存雪崩

缓存雪崩是指设置缓存时采用了相同的过期时间,导致缓存在某一个时刻同时失效,或者缓存服务器宕机宕机导致缓存全面失效,请求全部转发到了DB层面,DB由于瞬间压力增大而导致崩溃。缓存失效导致的雪崩效应对底层系统的冲击是很大的。 

解决方式

  1. 对缓存的访问,如果发现从缓存中取不到值,那么通过加锁或者队列的方式保证缓存的单进程操作,从而避免失效时并发请求全部落到底层的存储系统上;但是这种方式会带来性能上的损耗
  2. 将缓存失效的时间分散,降低每一个缓存过期时间的重复率
  3. 如果是因为缓存服务器故障导致的问题,一方面需要保证缓存服务器的高可用、另一方面,应用程序中可以采 用多级缓存

八、如何解决缓存穿透问题?

缓存穿透

缓存穿透是指查询一个根本不存在的数据,缓存和数据源都不会命中。出于容错的考虑,如果从数据层查不到数据 则不写入缓存,即数据源返回值为 null 时,不缓存 null。缓存穿透问题可能会使后端数据源负载加大,由于很多后端数据源不具备高并发性,甚至可能造成后端数据源宕掉

解决方式

  1. 如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访 问数据库,这种办法最简单粗暴。比如,”key” , “&&”。 在返回这个&&值的时候,我们的应用就可以认为这是不存在的key,那我们的应用就可以决定是否继续等待继续访 问,还是放弃掉这次操作。如果继续等待访问,过一个时间轮询点后,再次请求这个key,如果取到的值不再是 &&,则可以认为这时候key有值了,从而避免了透传到数据库,从而把大量的类似请求挡在了缓存之中
  2. 根据缓存数据Key的设计规则,将不符合规则的key进行过滤 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的BitSet中,不存在的数据将会被拦截掉,从而避免了 对底层存储系统的查询压力
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值