Redis常见题目整理

文章详细介绍了Redis的两种持久化机制RDB和AOF,包括它们的优缺点及应用场景。接着讨论了Redis的过期键删除策略,包括惰性过期和定期过期。此外,还阐述了Redis的单线程模型为何高效,以及事务的实现过程。最后,提到了主从复制的核心原理,包括全量复制和部分复制的流程,以及缓存穿透、缓存击穿和缓存雪崩的问题及解决方案。
摘要由CSDN通过智能技术生成

一、RDB AOF 机制

RDB:Redis DataBase
将某一个时刻的内存快照( Snapshot ),以二进制的方式写入磁盘。
实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
手动触发
  • save命令,使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所以在生产环境一定慎用
  • bgsave命令,fork出一个子进程执行持久化,主进程只在fork过程中有短暂的阻塞,子进程创建之后,主进程就可以响应客户端请求了。
自动触发
  • save m n配置 :在 m 秒内,如果有 n 个键发生改变,则自动触发持久化,通过bgsave执行,如果设置多个、只要满足其一就会触发,配置文件有默认配置(可以注释掉)
  • flushall:用于清空redis所有的数据库,flushdb清空当前redis所在库数据(默认是0号数据库),会清空RDB文件,同时也会生成dump.rdb、内容为空。
  • 主从同步全量同步时会自动触发bgsave命令,生成rdb发送给从节点。
优点:
1 、整个 Redis 数据库将只包含一个文件 dump.rdb ,方便持久化。
2 、容灾性好,方便备份。
3 、性能最大化, fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能。
4. 相对于数据集大时, AOF 的启动效率更高
缺点:
1 、数据安全性低。 RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候 )
2 、由于 RDB 是通过 fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟。

AOF:Append Only File
以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录,调操作系统命令进程刷盘。
默认关闭,在配置文件中开启。
  1. 所有的写命令会追加到 AOF 缓冲中。
  2. AOF 缓冲区根据对应的策略向硬盘进行同步操作。
  3. 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
  4. Redis 重启时,可以加载 AOF 文件进行数据恢复。
同步策略
每秒同步:异步完成,效率非常高,一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。
每修改同步:同步持久化,每次发生的数据变化都会被立即记录到磁盘中,最多丢一条。
不同步:由操作系统控制,可能丢失较多数据。

 优点:

1、数据安全,Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据 将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。

2 、通过 append 模式写文件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过 redis-check-aof 工具解决数据一致性问题。
3 AOF 机制的 rewrite 模式。定期对 AOF 文件进行重写,以达到压缩的目的。
缺点:
1 AOF 文件比 RDB 文件大,且恢复速度慢。
2 、数据集大的时候,比 rdb 启动效率低。
3 、运行效率没有 RDB 高。
AOF 文件比 RDB 更新频率高,优先使用 AOF 还原数据。
AOF RDB 更安全也更大。
RDB 性能比 AOF 好。
如果两个都配了优先加载 AOF。

二、Redis的过期键的删除策略

Redis key-value 数据库,我们可以设置 Redis 中缓存的 key 的过期时间。 Redis的过期策略就是指当Redis 中缓存的 key 过期了, Redis 如何处理。
  • 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
  • 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
expires 字典会保存所有设置了过期时间的 key 的过期时间数据,其中, key是指向键空间中的某个键的 指针, value 是该键的毫秒精度的 UNIX 时间戳表示的过期时间。键空间是指该 Redis集群中保存的所有键。
Redis中同时使用了惰性过期和定期过期两种过期策略。
定期扫描所有的数据库(16个),随机检查其中的key是否过期。

三、Redis线程模型、单线程快的原因

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器叫做文件事件处理器 file event handler。 这个文件事件处理器,它是单线程的,所以 Redis 才叫做单线程的模型,它采用 IO多路复用机制来同时监听多个 Socket ,根据 Socket上的事件类型来选择对应的事件处理器来处理这个事件。可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了 Redis 内部的线程模型的简单性。
文件事件处理器的结构包含 4 个部分:多个 Socket IO多路复用程序、文件事件分派器以及事件处理器 (命令请求处理器、命令回复处理器、连接应答处理器等)。
多个 Socket 可能并发的产生不同的操作,每个操作对应不同的文件事件,但是 IO多路复用程序会监听多个 Socket ,会将 Socket 放入一个队列中排队,每次从队列中取出一个 Socket 给事件分派器,事件分派器把 Socket 给对应的事件处理器。 然后一个 Socket 的事件处理完之后, IO 多路复用程序才会将队列中的下一个 Socket 给事件分派器。文件事件分派器会根据每个 Socket 当前产生的事件,来选择对应的事件处理器来处理。
单线程快的原因:
1 )纯内存操作
2 )核心是基于非阻塞的 IO 多路复用机制
3 )单线程反而避免了多线程的频繁上下文切换带来的性能问题

四、简述Redis事务实现

1 、事务开始
MULTI 命令的执行,标识着一个事务的开始。 MULTI 命令会将客户端状态的 flags 属性中打开REDIS_MULTI 标识来完成的。
2 、命令入队
当一个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执行不同的操作。如果客户端发送的命令为 MULTI EXEC WATCH DISCARD中的一个,立即执行这个命令,否则将命令放入一个事务队列里面,然后向客户端返回 QUEUED 回复。
  • 如果客户端发送的命令为 EXECDISCARDWATCHMULTI 四个命令的其中一个,那么服务器立即执行这个命令。
  • 如果客户端发送的是四个命令以外的其他命令,那么服务器并不立即执行这个命令。首先检查此命令的格式是否正确,如果不正确,服务器会在客户端状态(redisClient)的 flags 属性关闭 REDIS_MULTI 标识,并且返回错误信息给客户端。如果正确,将这个命令放入一个事务队列里面,然后向客户端返回 QUEUED 回复。
事务队列是按照 FIFO 的方式保存入队的命令。
3 、事务执行
  • 客户端发送 EXEC 命令,服务器执行 EXEC 命令逻辑。
  • 如果客户端状态的 flags 属性不包含 REDIS_MULTI 标识,或者包含 REDIS_DIRTY_CAS 或者 REDIS_DIRTY_EXEC 标识,那么就直接取消事务的执行。
  • 否则客户端处于事务状态(flags REDIS_MULTI 标识),服务器会遍历客户端的事务队列,然后执行事务队列中的所有命令,最后将返回结果全部返回给客户端;
redis 不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误。
Redis 事务不支持检查那些程序员自己逻辑错误。仅支持语法上的错误。例如对 String 类型的数据库键执行对 HashMap 类型的操作!
  • WATCH 命令是一个乐观锁,可以为 Redis 事务提供 check-and-set CAS)行为。可以监控一个或多个键一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC 命令。
  • MULTI命令用于开启一个事务,它总是返回OKMULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
  • EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil
  • 通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。
  • UNWATCH命令可以取消watch对所有key的监控

五、redis 主从复制的核心原理

通过执行 slaveof命令或设置slaveof选项,让一个服务器去复制另一个服务器的数据。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。
一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
  1. 从节点执行slaveof masterIp port,保存主节点信息。
  2. 从节点中的定时任务发现主节点信息,建立和主节点的socket连接。
  3. 从节点发送信号,主节点返回,两边能互相通信。
  4. 连接建立后,主节点将所有数据发送给从节点(数据同步)
  5. 主节点把当前的数据同步给从节点后,便完成了复制过程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。
runId 每个 redis 节点启动都会生成唯一的 uuid 每次 redis重启后,runId 都会发生变化
offset 主从节点各自维护自己的复制偏移量 offset 当主节点有写入命令时, offset=offset+命令的字节长度。从节点在收到主节点发送的命令后,也会增加自己的 offset ,并把自己的 offset发送给主节点。主节点同时保存自己的 offset 和从节点的 offset 通过对比offset来判断主从节点数据是否一致
repl_backlog_size 保存在主节点上的一个固定长度的先进先出队列,默认大小是 1MB
全量复制:
  • 从节点发送psync命令,psync runid offset(由于是第一次,runid为?,offset-1
  • 主节点返回FULLRESYNC runId offsetrunId是主节点的runIdoffset是主节点目前的offset。 从节点保存信息
  • 主节点启动bgsave命令fork子进程进行RDB持久化。
  • 主节点将RDB文件发送给从节点,到从节点加载数据完成之前,写命令写入缓冲区。
  • 从节点清理本地数据并加载RDB,如果开启了AOF会重写AOF。
1 )主节点通过 bgsave 命令 fork 子进程进行 RDB 持久化,该过程是非常消耗 CPU 、内存 ( 页表复制 )、硬盘 IO。
2 )主节点通过网络将 RDB 文件发送给从节点,对主从节点的带宽都会带来很大的消耗。
3 )从节点清空老数据、载入新 RDB文件的过程是阻塞的,无法响应客户端的命令;如果从节点执行 bgrewriteaof,也会带来额外的消耗。
部分复制:
1. 复制偏移量:执行复制的双方, 主从节点,分别会维护一个复制偏移量offset
2. 复制积压缓冲区: 主节点内部维护了一个固定长度的、先进先出(FIFO)队列作为复制积压缓冲区 ,当主从节点 offset 的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制
3. 服务器运行 ID(runid) :每个 Redis 节点,都有其运行 ID ,运行 ID由节点在启动时自动生成, 主节点会将自己的运行ID发送给从节点,从节点会将主节点的运行ID存起来 从节点Redis断开重连的时候,就是根据运行ID来判断同步的进度
  • 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况)
  • 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制
过程原理:

六、缓存穿透、缓存击穿、缓存雪崩

  • 缓存穿透
缓存穿透是指缓存和数据库中都没有的数据 ,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案:
  1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
  2. 缓存空对象。从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
  3. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。
  • 缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据一般是缓存时间到期的热点数据),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案
  1. 设置热点数据永远不过期。
  2. 加互斥锁。
  • 缓存雪崩

缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案:
  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  2. 给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效,则更新数据缓存。
  3. 缓存预热。
  4. 互斥锁。

七、如何保证数据库与缓存的一致性?

由于缓存和数据库是分开的,无法做到原子性的同时进行数据修改,可能出现缓存更新失败,或者数据库更新失败的情况,这时候会出现数据不一致,影响前端业务。
问题
  • 先更新数据库,再更新缓存。缓存可能更新失败,读到老数据。
  • 先删缓存,再更新数据库。并发时,读操作可能还是会将旧数据读回缓存。

解决方案

1》先操作缓存,但是不删除缓存,将缓存修改为一个特殊值(-999)。客户端读缓存时,发现是默认值,就休眠一小会,再去查一次Redis。 -》 特殊值对业务有侵入。 休眠时间,可能会多次重复,对性能有影响。

2》延时双删。 先删除缓存,然后再写数据库,休眠一小会,再次删除缓存。-》 如果数据写操作很频繁,同样还是会有脏数据的问题。

  • 先更新数据库,再删缓存。也存在缓存删除失败的可能。

解决方案

1》给缓存设置一个过期时间 问题:过期时间内,缓存数据不会更新。

2》引入MQ,保证原子操作。
将热点数据缓存设置为永不过期,但是在value当中写入一个逻辑上的过期时间,另外起一个后台线程,扫描这些key,对于已逻辑上过期的缓存,进行删除。
删除比更新操作更加轻量,是延迟加载的一种实现,更新可能涉及多个表、比较耗时。
终极方案:
将访问操作串行化
  1. 先删缓存,将更新数据库的操作放进有序队列中。
  2. 从缓存查不到的查询操作,都进入有序队列
会面临的问题:
  1. 读请求积压,大量超时,导致数据库的压力:限流、熔断。
  2. 如何避免大量请求积压:将队列水平拆分,提高并行度。
  3. 保证相同请求路由正确。

八、布隆过滤器原理,优缺点

位图:int[10] ,每个 int 类型的整数是 4*8=32 bit ,则 int[10] 一共有 320 bit ,每个 bit 0 1,初始化时都是 0
添加数据时,将数据进行hash得到hash值,对应到bit ,将该 bit 改为 1 hash函数可以定义多个,则一个数据添加会将多个( hash 函数个数) bit 改为 1 ,多个 hash 函数的目的是减少 hash碰撞的概率
查询数据:hash函数计算得到hash ,对应到 bit 中,如果有一个为 0 ,则说明数据不在 bit中,如果都为 1 ,则该数据可能在 bit 中。
优点:
  • 占用内存小
  • 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
  • 哈希函数相互之间没有关系,方便硬件并行运算
  • 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
  • 数据量很大时,布隆过滤器可以表示全集
  • 使用同一组散列函数的布隆过滤器可以进行交、并、差运算
缺点:
  • 误判率,即存在假阳性(False Position),不能准确判断元素是否在集合中
  • 不能获取元素本身
  • 一般情况下不能从布隆过滤器中删除元素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值