Redis面试必备

常见数据类型及应用场景

Redis 是一种高性能的键值存储系统,支持多种数据类型。以下是 Redis 常见的数据类型及它们各自的运用场景:

1.String (字符串)

  • 运用场景:String 是最基本的数据类型,在 Redis 中使用广泛。它可以存储字符串、整数或者浮点数,通常用于缓存、计数器、计分系统等场景。

2.List (列表)

  • 运用场景:List 是一个链表结构,可以存储多个字符串元素。常用于消息队列、简单的消息发布与订阅系统、最新消息排行榜等。

3.Set (集合)

  • 运用场景:Set 是一个无序集合,其中不允许重复的成员存在。适合用于存储唯一值,比如标签、共同好友等。

4.Sorted Set (有序集合)

  • 运用场景:Sorted Set 与 Set 类似,不同之处在于每个成员都关联一个分数(score),用于排序。适合用于排行榜、区间查询等场景。

5.Hash (哈希)

  • 运用场景:Hash 是一个键值对集合,其中的值也是一个键值对集合。常用于存储对象,比如用户信息、商品信息等,可以实现类似关系型数据库的行存储。

6.Bitmap (位图)

  • 运用场景:Bitmap 是一种特殊的字符串数据类型,可以进行位运算。适合用于标记用户是否在线、统计用户活跃度等场景。

7.HyperLogLog (基数估算)

  • 运用场景:HyperLogLog 用于估算集合中不重复元素的数量,适合用于统计网站每天的独立访客数量等。

8.Geospatial (地理空间索引)

  • 运用场景:Geospatial 数据类型用于存储地理位置信息,可以进行附近位置搜索、地理围栏判断等。

redis数据持久化的两种方式及各自的优缺点

Redis 支持两种主要的数据持久化方式:RDB(Redis DataBase)和 AOF(Append Only File)。

1.RDB (Redis DataBase)

  • 优点

    • 性能较高:RDB 是将 Redis 在某个时间点的数据快照以二进制格式写入磁盘,因此在数据恢复时速度较快。

    • 文件紧凑:RDB 文件通常比 AOF 文件小,因为它们只包含了快照的数据。

    • 适合备份:RDB 文件可以方便地用于备份和迁移。

  • 缺点

    • 可能丢失数据:RDB 是周期性地保存数据快照,如果 Redis 在最后一次保存之后发生崩溃,则会丢失最后一次保存之后的所有数据。

    • 内存数据的全量同步:RDB 备份需要将内存中的数据全量同步到磁盘,如果数据量过大,可能会导致 IO 负载过重,影响服务器的性能。

    • fork 的性能影响:RDB 备份依赖于主进程的 fork 操作,对于较大的数据集来说,fork 操作可能会导致服务请求的瞬间延迟。在 fork 时,内存中的数据被克隆了一份,大致会导致两倍的内存膨胀,需要谨慎考虑和规划。

  1. AOF (Append Only File)
  • 优点: - 数据安全:AOF 记录了 Redis 服务器所执行的写命令,因此在崩溃恢复时能够尽可能地保护数据。 - 恢复精确度高:AOF 文件中记录了服务器执行的每个写命令,因此在恢复数据时能够达到比较高的精确度。
    • 适合持久化大规模数据:AOF 在持久化大规模数据时可以实现较低的阻塞时间,因为它是在后台以追加方式写入文件的。
  • 缺点: - 文件较大:AOF 文件通常比 RDB 文件大,因为它包含了每个写命令的记录。 - 恢复速度较慢:在数据恢复时,AOF 文件需要重放所有写命令,因此在数据量较大时恢复速度可能会较慢。 - 对于相同的数据,AOF 持久化方式相对于 RDB 会有一些性能损失,因为它需要不断地追加写入日志。

综合来看,RDB 适用于要求数据恢复速度快、备份频率较低且数据量不是很大的场景;而 AOF 则适用于对数据持久性要求更高、可以接受稍微较大的数据文件、并且能够容忍稍慢的数据恢复速度的场景。

什么是redis事务

Redis 事务是一种将多个命令打包在一起执行的机制,可以确保这些命令在执行期间不会被其他客户端的命令所打断,从而保证了原子性。Redis 事务使用 MULTI、EXEC、DISCARD 和 WATCH 等命令来实现。

以下是 Redis 事务的详细介绍:

  1. MULTI 命令:事务的开始。当客户端发送 MULTI 命令时,Redis 会将之后发送的命令都存储在一个队列中,但不会立即执行。如果客户端发送了 MULTI 命令,但又没有发送 EXEC 命令,则队列中的所有命令都会被丢弃。

  2. 命令入队:在 MULTI 和 EXEC 之间发送的命令都会被放入一个队列中,但不会立即执行。

  3. EXEC 命令:执行事务中的所有命令。当客户端发送 EXEC 命令时,Redis 会按照命令入队的顺序执行队列中的所有命令。如果在执行事务期间发生错误,则 Redis 会继续执行后续的命令,直到事务结束。执行事务后,Redis 会将事务中的所有命令的执行结果返回给客户端。

  4. DISCARD 命令:取消事务。当客户端发送 DISCARD 命令时,Redis 会丢弃事务队列中的所有命令,并清除事务状态,使其回到非事务状态。

  5. WATCH 命令:监视键。当客户端发送 WATCH 命令并指定一个或多个键时,如果在事务执行之前这些键被其他客户端修改,则事务将被取消。WATCH 命令可以用于实现乐观锁。

    “WATCH” 命令在 Redis 中用于实现乐观锁的概念。乐观锁是一种并发控制机制,它假设在并发访问的情况下,冲突是比较少见的,因此不会立即对资源进行加锁,而是在更新资源时检查是否存在冲突。如果存在冲突,乐观锁会回退并重试,直到成功。

    在 Redis 中,使用 WATCH 命令可以监视一个或多个键,然后在执行事务时检查这些键是否被其他客户端修改。如果在事务执行期间,有任何被监视的键被其他客户端修改,那么该事务会被取消。这种机制类似于乐观锁的检查过程。

    通过结合 MULTI 和 EXEC 命令,可以在 WATCH 的基础上构建一个原子性的事务。这样,当多个客户端尝试同时修改某个资源时,可以使用 WATCH 来确保事务的原子性,以避免竞态条件和数据不一致的问题。

  6. 如果在 Redis 事务中,任何一个命令在语法上存在错误,Redis 会立即返回一个错误响应,并且不会执行后续的命令。这意味着即使其他命令是正确的,它们也不会被执行。(全体连坐

  7. 但是,如果所有命令的语法检查都通过了,那么在执行事务期间,如果有某个命令执行失败,它不会影响其他命令的执行。其他命令仍然会被继续执行,并且事务会继续执行到结束。(冤头债主

Redis事务 VS 数据库事务

1. 单独的隔离操作 Redis 的事务仅仅是保证事务里的操作会被连续独占的执行,redis 命令执行是单线程架构,在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的
2. 没有隔离级别的概念 因为事务提交前任何指令都不会被实际执行,也就不存在 ”事务内的查询要看到事务里的更新,在事务外查询不能看到” 这种问题了
3. 不保证原子性 Redis 的事务不保证原子性,也就是不保证所有指令同时成功或同时失败,只有决定是否开始执行全部指令的能力,没有执行到一半进行回滚的能力
4. 排它性 Redis 会保证一个事务内的命令依次执行,而不会被其它命令插入

redis管道

管道(pipeline)可以一次性发送多条命令给服务端,服务端依次处理完毕后,通过一 条响应一次性将结果返回,通过减少客户端与redis的通信次数来实现降低往返延时时间。pipeline 实现的原理是队列,先进先出特性就保证数据的顺序性。

管道是为了解决以下问题:

  1. 减少网络延迟: 在进行批量操作时,使用管道可以减少由于网络通信造成的延迟。因为管道可以将多个命令打包发送到 Redis 服务器,减少了每个命令发送和接收的时间。
  2. 提高吞吐量: 通过减少网络通信的次数,管道可以提高 Redis 服务器的吞吐量,尤其是在需要执行大量命令的场景下,如批量写入、批量读取等。

使用 pipeline 注意事项

  1. pipeline 缓冲的指令只是会依次执行,不保证原子性,如果执行中指令发生异常,将会继续执行后续的指令。
  2. 使用 pipeline 组装的命令个数不能太多,不然数量过大客户端阻塞的时间可能过久,同时服务端此时也被迫回复一个队列答复,占用很多内存。

主从复制

原理

slave 启动,同步初清

  • slave 启动成功链接到 master 后会发送一个 sync 命令
  • slave 首次全新连接 master,一次完全同步(全量复制)将被自动执行,slave 自身原有数据会被 master 数据覆盖清除。

首次连接,全量复制

  • master 节点收到 sync 命令后会开始在后台保存快照(即 RDB 持久化,主从复制时会触发 RDB),同时收集所有接收到的用于修改数据集的命令并缓存起来,master 节点执行 RDB 持久化完后,master 将 RDB 快照文件和所有缓存的命令发送到所有 slave,以完成一次完全同步。
  • 而 slave 服务在接收到数据库文件数据后,将其存盘并加载到内存中,从而完成复制初始化。

心跳持续,保持通信

  • repl-ping-replica-period 10:主节点将每隔 10 秒向从节点发送一个心跳检测的包,以确认从节点是否在线。

进入平稳,增量复制

  • master 继续将新的所有收集到的修改命令自动依次传送给 slave,完成同步。

从机下线,重连续传

  • master 会检查 backlog 里面的 offset,master 和 slave 都会保存一个复制的 offset 还有一个 masterId,offset 是保存在 backlog 中 的。master 只会把已经缓存的 offset 后面的数据复制给 slave,类似断点续传。

缺点

  • 复制延时,信号衰减

    由于所有的写操作都是先在 Master 上操作,然后同步更新到 Slave 上,所以从 Master 同步到 Slave 机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave 机器数量的增加也会使这个问题更加严重。

  • master 挂了怎么办?

    默认情况下,不会在 slave 节点中自动选一个 master

    那每次都要人工干预? —> 无人值守变成刚需

哨兵

运行流程

当一个主从配置中 master 失效后,sentinel 可以选举出一个新的 master 用于自动接替原 master 的工作,主从配置中的其他 redis 服务器自动指向新的 master 同步数据,一般建议 sentinel 采取奇数台,防止某一台 sentinel 无法连接到 master 导致误切换

  1. 主观下线

    SDOWN(主观不可用)是单个 sentinel 自己主观上检测到的关于 master 的状态,从 sentinel 的角度来看,如果发送了 PING 心跳后,在一定时间内没有收到合法的回复,就达到了 SDOWN 的条件。

  2. 客观下线

​ ODOWN 需要一定数量的 sentinel,多个哨兵达成一致意见,才能认为一个 master 客观上已经宕机

  1. 当主节点被判断客观下线后,各个哨兵节点通过Raft 算法,先选举出一个领导者哨兵节点并由该领导者进行 failover(故障转移)。

  2. 领导者哨兵节点开始推动故障切换流程并选出新的 master。

新主机的选举规则
  1. redis.conf 文件中,优先级 slave-priority 或者 replica-priority 最高的从节点 (数字越小优先级越高)

  2. 复制偏移位置 offset 最大的从节点 (也就是在 master 还没有宕机时,复制到数据比其他 Slave 要多)

  3. 最小 Run ID 的从节点,字典顺序,ASCII 码。

  1. 通过 slaveof 命令让其他节点成为其从节点。

  2. 老 master 重新上线后,会将它设置为新选出的 master 的从节点。

集群

介绍

Redis 集群是 Redis 提供的一种分布式解决方案,用于在多个 Redis 节点之间分配数据并提供高可用性和可扩展性。Redis 集群主要用于解决单机 Redis 存储容量有限、性能瓶颈等问题,通过将数据分布到多个节点上来提高存储容量和处理能力。

下面是 Redis 集群的一些特点和关键概念:

特点:

  1. 分布式存储: Redis 集群将数据分布存储在多个节点上,每个节点存储部分数据,从而提高了整个系统的存储容量。
  2. 高可用性: Redis 集群采用主从复制和自动故障转移机制,能够在主节点故障时自动切换到从节点,保证系统的高可用性。
  3. 可扩展性: Redis 集群支持动态添加或移除节点,能够根据需求动态扩展存储容量和处理能力。
  4. 分片: Redis 集群将数据分片存储在多个节点上,每个节点负责存储其中一部分数据,从而实现了数据的水平分片。

关键概念:

  1. 节点(Node): Redis 集群中的每个 Redis 实例称为一个节点,每个节点都可以是主节点或者从节点。

  2. 槽(Slot): Redis 集群将数据分为 16384 个槽位(slot),每个槽位都有一个唯一的编号,每个槽位可以存储一个键值对。

  3. 分片(Sharding): Redis 集群将数据根据哈希算法分配到不同的槽位上,从而实现数据的分片存储。

  4. 主节点(Master): Redis 集群中负责处理写操作和负责槽位分配的节点称为主节点。

  5. 从节点(Slave): Redis 集群中负责复制主节点数据的节点称为从节点,从节点用于提供读操作和故障转移。

  6. 故障转移: 当主节点不可用时,Redis 集群会自动将一个从节点晋升为主节点,保证系统的可用性。

通过以上特点和关键概念,Redis 集群能够提供高性能、高可用性和可扩展性的分布式存储解决方案。

slot 槽位映射方案

slot 槽位映射,一般业界有三种解决方案

哈希取余分区

2 亿条记录就是 2 亿个 k,v,我们单机不行必须要分布式多机,假设有 3 台机器构成一个集群,用户每次读写操作都是根据公式:hash (key) % N 个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。

优点:简单粗暴,直接有效,只需要预估好数据规划好节点,例如 3 台、8 台、10 台,就能保证一段时间的数据 支撑。使用 Hash 算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求 (并维护这些请求的信息), 起到负载均衡 + 分而治之的作用。

缺点:扩缩容较为麻烦,每次数据变动导致节点有变动,映射关系需要重新进行计算。

一致性哈希算法分区

一致性哈希算法分区是为了解决分布式缓存数据变动和映射问题。构建一个有 $2^{32}-1$ 个位置的线性空间,将元素的哈希值对 $2^{32}-1$ 取模,确定该服务器结点和元素在哈希环上的位置。

  1. 服务器 IP 节点映射:将集群中各个 IP 节点映射到环上的某一个位置。

  2. 落到服务器的落键规则:首先计算 key 的 hash 值,hash (key),将这个 key 使用相同的函数 Hash 计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针 “行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。

优点

  • 一致性哈希算法的容错性

    假设 Node C 宕机,此时对象 A、B、D 不会受到影响,受到影响的只是 B、C 之间的数据且这些数据会转移到 D 进行存储

  • 一致性哈希算法的扩展性

    数据量增加了,需要增加一台节点 NodeX,X 的位置在 A 和 B 之间,那收到影响的也就是 A 到 X 之间的数据,重新把 A 到 X 的数据录入到 X 上即可,不会导致 hash 取余全部数据重新洗牌。

缺点

一致性哈希算法的数据倾斜问题:一致性 Hash 算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上) 问题。

哈希槽分区

为了解决均匀分配的问题,在 数据和节点之间又加入了一层,把这层称为哈希槽 (slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里面放的是数据

槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。哈希解决的是映射问题,使用 key 的哈希值来计算所在的槽,便于数据分配。

一个集群只能有 16384 个槽,编号 0-16383 (0~$2^{14}-1$)。这些槽会分配给集群中的所有主节点,分配策略没有要求。

redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。

集群会记录节点和槽的对应关系,解决了节点和槽的关系后,接下来就需要对 key 求哈希值,然后对 16384 取模,余数是几 key 就落入对应的槽里。HASH_SLOT = CRC16 (key) mod 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。

经典面试题

为什么 Redis 集群的最大槽数是 16384 个?

(1) 如果槽位为 65536,发送心跳信息的消息头达 8k,发送的心跳包过于庞大。

每秒钟,redis 节点需要发送一定数量的 ping 消息作为心跳包,如果槽位为 65536,这个 ping 消息的消息头太大了,浪费带宽。

(2) redis 的集群主节点数量基本不可能超过 1000 个。

集群节点越多,心跳包的消息体内携带的数据越多。如果节点过 1000 个,也会导致网络拥堵。因此 redis 作者不建议 redis cluster 节点数量超过 1000 个。那么,对于节点数在 1000 以内的 redis cluster 集群,16384 个槽位够用了,没有必要拓展到 65536 个。

(3) 槽位越小,节点少的情况下,压缩比高,容易传输。

Redis 主节点的配置信息中它所负责的哈希槽是通过一张 bitmap 的形式来保存的,在传输过程中会对 bitmap 进行压缩,但是如果 bitmap 的填充率 slots / N 很高的话 (N 表示节点数), bitmap 的压缩率就很低。如果节点数很少,而哈希槽数量很多的话,bitmap 的压缩率就很低。

0%