Redis 面试题:缓存一致性、持久化与线程模型
Redis 常用于缓存、计数和排行榜。讨论缓存一致性时,要说明数据库提交与缓存失效的顺序,以及失败后如何恢复;讨论持久化时,要说明重启后能恢复到哪个时间点。这两类问题不能用“单线程”或“延时双删”一概回答。
Redis
1、Redis支持哪些数据类型及应用场景?
- String:字符串、整数、浮点型,可用于计数器、分布式锁
- Hash:包含键值对的无序散列表,可用于存储用户信息对象(User对象)
- List:列表,可用于存储集合,消息队列
- Set:无序集合不可重复,可用于好友关系(Set交并差集)
- Zset:有序集合不可重复,可用于排行榜(zset的score)
2、缓存和数据库的一致性
常用起点是 Cache-Aside:读缓存,未命中再查数据库并带 TTL 回填;写入先提交数据库事务,再删除对应缓存。并发量高并不是改成“先删缓存”的依据。 这与 Redis 官方 Jedis 缓存示例 的失效思路一致,但两套存储之间没有天然的原子提交,需要按业务容忍的陈旧程度设计补偿。
删除应发生在数据库事务成功提交后;仅把 DEL 写在事务方法的 SQL 后面,可能仍早于提交。提交后回调能调整顺序,但不能消除“提交成功后进程崩溃,尚未发出删除”的窗口。
| 失败路径 | 会发生什么 | 应对思路与边界 |
|---|---|---|
| 先删缓存,数据库还没提交 | 并发读未命中,读出数据库旧值并回填;后续提交不会自动清掉这份旧缓存。 | 不把先删作为高并发通用解法;写后失效仍要有失败补偿。 |
| 数据库提交成功,删缓存超时、失败或进程崩溃 | 超时意味着结果未知;若删除未生效,旧缓存继续被命中,单次同步重试也可能随进程退出而丢失。 | 可在同一数据库事务中写入 outbox 待办,或通过 CDC 捕获已提交变更,再可靠投递失效任务;消费端幂等删除、重试、监控积压并对失败告警。 |
| 读请求已读出旧值,但回填较慢 | 读 A 未命中并读出旧值;写 B 提交新值且删除缓存;A 最后又把旧值写回。 | 成功删除也不能排除此竞态。可按需求增加延迟失效、版本校验或覆盖读写路径的同 key 协调;仅锁写请求挡不住读回填。 |
| 缓存未命中后读了延迟副本 | 即使提交后才回填,副本也可能返回旧数据。 | 对需要读己之写的链路路由到主库,或结合版本、复制进度判断;删缓存不解决副本延迟。 |
延时双删的边界: 常见流程是“删缓存 → 更新并提交数据库 → 延迟再删”,第二次删除用于清理并发回填的旧值;它不要求最后再主动写缓存。固定休眠 500ms 无法覆盖所有慢请求和复制延迟,第二次删除也会失败。若采用,应使用可重试的异步任务,结合实际延迟评估时间,并保留 TTL 等兜底。
分布式锁只有在相关读回填与写入路径都遵守同一协议、并正确处理租约过期等情况时才有意义,不能让数据库和 Redis 自动变成一个事务。TTL 限制单次缓存值的存活时间,也不能单独给出从数据库提交起计算的严格一致性上界。余额、库存最终判定等要求强一致的操作,应由权威存储及其事务约束完成。
面试或项目验收时,可以主动复现“提交后 DEL 失败”“暂停旧读,等写后删除完成再恢复回填”“读副本滞后”三个场景,记录陈旧数据持续时间、重试是否恢复及告警是否触发。上表是基于 Cache-Aside 时序的工程分析,具体补偿机制需按系统约束选择。
3、Redis的持久化机制
持久化把内存状态写入持久存储,用于重启恢复;它降低数据丢失风险,不代表任意故障下零丢失。以下按 Redis Open Source 的 RDB/AOF 机制说明,配置及文件布局需对照实际版本,不能把发行包默认值当成所有部署的设置。
| 方式 | 保存内容与代价 | 恢复和数据丢失边界 |
|---|---|---|
| RDB | 按 save 条件或手动触发生成时间点快照;BGSAVE 使用子进程及写时复制,文件紧凑,通常适合备份与较快恢复。 | 只能恢复到最近一次成功快照所包含的状态,后续写入可能丢失;fork 停顿、写时复制额外内存和磁盘 I/O 都有成本。 |
| AOF | 记录改变数据的写操作,而非所有查询;恢复时加载基础状态并重放后续日志。写盘策略决定延迟与耐久性的取舍。 | appendfsync always 在回复前等待相应写入及同步,可合并一批命令;everysec 通常后台每秒同步,故障时可能损失约一秒写入;no 由操作系统决定何时同步,丢失窗口更不可控。 |
“命令已经执行”“写入操作系统页缓存”“fsync 完成”是不同阶段,收到成功响应也要结合策略判断是否已落盘。everysec 的约一秒是正常工作假设下的量级,不是磁盘阻塞、配置变化等所有故障下的绝对上限;硬件故障也不能仅靠 always 消除。
- RDB 与 AOF 同时开启:标准启动恢复时通常优先加载 AOF,RDB 可用于独立快照备份;开启两者并不等于两份数据永远包含同样的新写入。
- 混合 AOF 与上述概念不同:Redis 4.0 起支持在 AOF 重写产物中使用 RDB 格式前导,再接增量 AOF;它由
aof-use-rdb-preamble等配置控制,不是“同时开启 RDB/AOF”的别名。 - Redis 7.0 起的多文件 AOF:基础文件、增量文件由 manifest 管理,基础文件可采用 RDB 或 AOF 格式,不能再把备份理解为只复制一个
appendonly.aof。 - 选择与验收:可重建缓存可按需求关闭持久化,但要评估冷启动回源压力;不可重建的数据应明确可接受丢失窗口和恢复耗时,并做备份恢复演练。持久化不解决数据库与缓存一致性,也不等于复制或故障切换的零丢失保证。
依据:Redis persistence;磁盘同步和延迟细节见 Diagnosing latency issues。
4、Redis中key的过期策略
- 定期删除:指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。假设redis里放了10万个key,都设置了过期时间,你每隔几百毫秒,就检查10万个key,那redis基本上就死了,cpu负载会很高的,消耗在你的检查过期key上了。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样就是一场性能上的灾难。实际上redis是每隔100ms随机抽取一些key来检查和删除的。
- 惰性删除:定期删除可能会导致很多过期key到了时间并没有被删除掉,那么惰性删除就是在你获取某个key的时候,redis会检查一下 ,这个key如果过期了就删除,不会给你返回任何东西。
5、Redis的淘汰策略(了解)
- 如果redis的内存占用过多的时候,此时会进行内存淘汰策略。
- volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰;
- volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰;
- volatile-random:从已设置过期时间的数据集中任意选择数据淘汰;
- allkeys-lru:从数据集中挑选最近最少使用的数据淘汰;
- allkeys-random:从数据集中任意选择数据淘汰;
- noeviction:不主动淘汰键;达到 maxmemory 且无法腾出空间时,可能增加内存的写命令返回错误,读命令仍可执行(Redis 7/8 默认策略)。
6、缓存雪崩、缓存穿透、缓存击穿、缓存预热、缓存降级
- 缓存雪崩:缓存大面积同时间段失效 ,请求都去查询数据库了,对数据库CPU和内存造成巨大压力。
- 解决办法:
- 将缓存失效时间分散开;
- 考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。
- 缓存穿透:缓存穿透指的是当查询一个数据库中不存在的数据时,由于缓存层没有存储这个数据的记录(即缓存未命中),因此每次查询都会穿透到数据库层,导致数据库承受不必要的压力。
- 解决办法:
- 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
- 也可以将查询返回的数据为空的这个结果进行缓存,过期时间设置短一点短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库。
- 缓存击穿:某个 key 非常热点,访问非常的频繁,高并发访问的情况下,当这个 key在失效的瞬间,大量的请求进来,这时候就击穿了缓存,直接请求到了数据库。
- 解决办法:
- 热点数据永不过期。
- 分布式锁,来个请求先锁住,然后去db查,查到后再将数据set到redis里。只有当redis里getKey没拿到数据需要请求db的时候才加锁。
- 缓存预热:就是系统启动的时候就查询数据库将相关的数据直接缓存到Redis中。这样可以避免用户查询的时候先判断是否有缓存,没的话在查db进行缓存等一系列操作了。
- 实现方案
- 如果数据量不大的话,可以在项目启动的时候进行缓存。
- 如果数据量很大的话可以写个简易的页面,上线时手工点一下进行缓存。
- 定时任务定时刷新缓存。
- 缓存降级:就是当缓存出故障的时候,别影响主业务系统,让他可以继续对外提供服务
- 实现方案
- 很简单,在catch里去查db,而不是抛出异常。
- 利用熔断降级组件返回错误码。
7、Redis是单线程的吗
- 对 Redis Open Source 的常规键值命令,通常所说的“单线程”指核心命令执行主要在主线程串行进行,不是整个进程的所有操作都只有一个线程。
- Redis 6.0 起支持通过配置使用多线程网络 I/O,分担 socket 读写等工作;这不等于同一实例中的普通命令都能并行执行。
- AOF 的后台
fsync、异步释放内存等可交给后台线程;BGSAVE和后台 AOF 重写通常使用 fork 子进程,不要把子进程称为“另一个线程”。 - 内存访问、合适的数据结构和 I/O 多路复用降低了请求处理开销;瓶颈可能来自网络、内存带宽、慢命令或 CPU,需结合负载测量。长时间占用命令线程的操作仍会阻塞其他请求。
版本依据:Redis 6 官方介绍的 threaded I/O;持久化线程与子进程见第 3、11、13 题。
8、Redis的setnx命令原理(重要)
- SETNX即Set if Not eXists,setnx是redis的一个命令,它会检查指定的键是否已经存在于数据库中,如果键不存在,则将该键设置指定的值,并返回1,表示操作成功。如果键已经存在,SETNX命令将不执行任何操作,返回0,表示操作失败。
- 这个原理保证了在多个客户端同时尝试设置同一个键时,只有一个客户端能够成功执行设置操作,其他客户端将得到失败的返回值。这使得setnx 可以用于实现分布式锁。
9、列举几个Redis应用场景?
- 计数器
- 数据缓存
- 页面缓存
- 消息队列(blpop)
- 分布式锁
- 好友关系(set交并差集)
- 排行榜(zset的score)
10、Redis线程模型知道吗?(了解)
- Redis 的事件循环通过 I/O 多路复用监听多个 socket,把就绪事件交给对应处理器处理;并发连接不等于命令并行执行。传统模型在主线程推进事件与命令处理,Redis 6.0 起还可配置 I/O 线程分担网络读写,核心命令执行仍主要由主线程串行完成,边界见第 7 题。
11、RDB持久化的原理是怎样的?(了解)
- RDB(Redis DataBase)持久化将内存中的数据集快照(Snapshot)写入磁盘。常用的
BGSAVE通过 fork 子进程完成,子进程共享父进程的内存页,把快照写入临时文件,完成后替换上一次的 RDB 文件;SAVE则同步生成快照,会阻塞命令处理,不能把所有 RDB 保存都说成后台执行。 - 在RDB持久化的过程中,主进程(父进程)仍然可以继续处理客户端的请求,而子进程则负责完成持久化的工作。由于子进程是父进程的一个副本,因此它包含了父进程在fork时刻的内存数据状态。所以,即使主进程在持久化过程中继续处理请求并修改了数据,这些修改也不会影响子进程进行持久化的数据状态。
- RDB持久化的优点在于它生成的文件是一个紧凑的二进制文件,可以很方便地进行备份和传输。而且,由于它是基于快照的持久化方式,因此可以很好地支持数据的恢复。但是,RDB持久化也有一些缺点。首先,由于它是在某个时间点上对整个内存数据进行快照,因此可能会丢失在这个时间点之后的数据修改。其次,在进行RDB持久化时,会消耗一定的系统资源(如CPU和内存),可能会影响Redis的性能。
12、为什么要用copyonwrite?(了解)
- fork 后父子进程起初共享物理内存页;父进程继续写数据时,操作系统对被修改的页进行复制,使子进程仍看到 fork 时的快照状态,而不用一开始就完整复制整个数据集。
- 例如 fork 时
k1=1、k2=2,之后主进程把它们改为11、22,子进程保存的仍是快照中的1、2,不会因父进程后续写入而混入新旧状态。 - 写时复制不是零成本:fork 仍需处理页表,持续写入会增加额外内存。快照不包含 fork 后的新写入,这正是 RDB 的恢复边界之一。
13、AOF持久化的原理是怎样的?(了解)
- 修改数据的命令执行后,Redis 将相应操作追加到 AOF 缓冲区,再写入文件;
write到操作系统缓存与fsync同步到持久存储是两回事,并不是每条命令都会立即刷盘。 appendfsync的正式策略是always、everysec、no,不是“缓冲区满”三选一。always等待同步后才回复,everysec通常由后台线程执行同步;磁盘慢、写入或 fork 仍可能影响主线程延迟,不能把请求处理和文件写入描述成一个不可失败的原子操作。- AOF 重写根据当前数据生成较紧凑的恢复表示,后台重写通常由子进程完成,期间继续接收新写入。Redis 7.0 起通过基础文件、增量文件及 manifest 协作,详见第 3 题;重写后的 AOF 不是保留全部历史命令的审计日志。
14、Redis的事务概念和原理
- Redis的事务并不像Mysql那么灵活,有隔离级别,出问题后还能回滚数据等高级操作。Redis毕竟是非关系型数据库,他目前事务回滚机制是不执行命令,也就是可以采取watch命令模拟乐观锁,进行监听数据,发现数据不是事务开始时候的样子了,那么我这个事务里的命令就不会得到执行。
- 原理:
- watch
- 每个数据库都维护一个字典watched_keys,key为监听的key,value即为监听的客户端对象。 所有客户端的修改都会去检查这个字典,如果key一致则会去打开客户端对象的 REDIS_DIRTY_CAS.
- multi 和 exec
- multi命令将客户端标记为“事物状态”,这个客户端后续的普通操作指令都会放入事务队列(客户端会保存一个事务队列),直到执行multi、exec、watch(?)、discard。
- exec时会服务端去查当前客户端的REDIS_DIRTY_CAS,如果被打开了,则拒绝客户端提交的事务。没打开则遍历客户端事务队列,挨条执行。
15、Redis的哨兵机制
- 哨兵的核心功能:在主从复制的基础上,哨兵引入了主节点的自动故障转移。
- Sentinel 用 PING 等监测节点。单个 Sentinel 的判断是主观下线 SDOWN;对主库形成客观下线 ODOWN,需要至少配置 quorum 数量的 Sentinel 在规定时间内同意,不能只说“多个节点同意”。
- 执行故障转移前,候选 Sentinel 还须取得多数 Sentinel 且满足 quorum 所要求的授权,选举对象是主持切换的 Sentinel。它再从合格副本中按 replica-priority、复制偏移、run ID 等规则选择新主;不是让副本彼此投票。quorum=2 的五哨兵部署仍需要取得至少三票的多数授权。
- 完整故障链路与条件见Sentinel 与 Cluster 进阶,依据 Redis Sentinel 官方说明。
16、Redis主从复制(了解)
主从复制是指将一台 Redis 服务器的数据复制到其他的 Redis 服务器。前者称为主节点(Master),后者称为从节点(Slave);数据的复制是单向的,只能由主节点到从节点。 默认情况下,每台 Redis 服务器都是主节点,且一个主节点可以有多个从节点 (或没有从节点),但一个从节点只能有一个主节点。
主从复制实现了数据的热备份;当主节点出现问题时,可以由从节点提供服务,实现故障的快速恢复;在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务 (即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。主从复制是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
以下复制机制适用于 Redis 7.x / 8.0。SYNC是不支持部分重同步的旧协议;Redis 2.8 起使用PSYNC,Redis 4.0 起的 PSYNC2 又支持故障切换后的历史衔接。不能把每次重连都描述成重新生成并传输完整 RDB。首次连接没有可复用历史时,从节点可发送
PSYNC ? -1,主节点以FULLRESYNC <replid> <offset>开始全量同步:传送 RDB 快照,再补发快照期间的复制流,最后持续增量复制。快照期间的新操作缓存在内存复制缓冲中,不是追加进 RDB。磁盘式传输可先生成文件;配置无盘复制时,子进程可直接通过网络发送 RDB,从节点是否落盘加载也取决于配置。断线重连时,从节点携带复制 ID 和所需的下一个字节偏移;只有主节点认可该历史,且所需数据仍在 backlog 中,才以
CONTINUE部分重同步。否则回退全量同步。故障切换后,新主保留旧复制 ID 为第二 ID,并记录可接受的偏移上界;因此换主不必然全量,但也不能保证一定部分同步。复制偏移是复制流的字节位置,不是命令条数、时间戳或已落盘位置。 复制 ID 标识一段数据历史,偏移只有在可兼容的历史中才可比较;从节点的
REPLCONF ACK <offset>报告已处理进度,不代表已经持久化。复制积压缓冲区(backlog) 是主节点保留的近期复制流窗口,供断线节点追赶,由
repl-backlog-size调整保留规模;没有从节点连接一段时间后还可能按repl-backlog-ttl释放。Redis 7.0 起 backlog 与从节点发送缓冲共享底层复制缓冲块,不能简单按“每个从节点一份固定大小环形数组”计算内存。慢从节点仍可能使复制缓冲增长,并受输出缓冲限制影响。
同一复制历史,主节点当前偏移 1500,backlog 保留字节 [1001, 1500]
从节点已处理到 1200 → PSYNC <replid> 1201 → 可补发 [1201, 1500]
从节点只处理到 900 → PSYNC <replid> 901 → 缺失字节已被淘汰,需全量同步backlog 容量可按“复制流峰值字节速率 × 预计断线时长 + 余量”估算;这是容量规划思路,不能保证任意断线都免全量。主从默认异步复制,读从节点可能读到旧值,故障切换也可能丢失已向客户端确认的写入。依据:官方复制机制、Redis 7.0 复制缓冲实现及 Redis 8.0 复制配置。
17、Redis的集群(了解)
什么是 Redis 集群
简单理解,Redis 集群是由多个 Redis 实例协同工作,以完成相同的任务或在高并发情况下分担负载。Redis Cluster 支持分片及主节点故障后的副本提升,但异步复制下故障切换仍可能丢失已确认写入,不能据此承诺强一致或数据完整不丢失。
Redis 的部署模式
Redis 提供了以下三种主要的部署模式,以满足不同的高可用性和扩展性需求:
主从复制模式(Master-Slave Replication)
哨兵模式(Sentinel Mode)
集群模式(Cluster Mode)
主从复制模式(Master-Slave Replication)
描述:
- 主节点(Master):负责处理所有的写操作和部分读操作。
- 从节点(Slave):复制主节点的数据和状态信息,仅用于数据备份和分担读请求。
特点:
- 数据冗余:通过多个从节点备份主节点的数据,提高数据的可靠性。
- 读写分离:将读请求分发到从节点,提升读性能。
- 手动故障切换:当主节点故障时,需要手动将某个从节点提升为新的主节点。
适用场景:
- 数据备份和恢复。
- 读操作较多的应用,通过从节点分担读压力。
- 哨兵模式(Sentinel Mode)
描述:
- 基于主从复制模式,增加了 哨兵(Sentinel) 节点来监控 Redis 集群的状态。
- 哨兵节点负责自动检测主节点的故障,并在故障发生时自动进行主从切换。
特点:
- 高可用性:自动故障检测与切换,减少人工干预。
- 监控与通知:实时监控 Redis 节点的状态,并在必要时发送通知。
- 不支持数据分片:仍然受限于单机的存储容量。
适用场景:
- 需要高可用性的应用,但数据量在单机可承受范围内。
- 希望简化运维,减少手动故障处理的场景。
- 集群模式(Cluster Mode)
描述:
- Redis Cluster 通过 数据分片(Sharding) 将数据分布到多个主节点,每个主节点可以有一个或多个从节点。
- 集群中的每个主节点负责部分键空间,实现水平扩展。
特点:
- 数据分片:打破单机内存限制,将数据分布到多个节点,支持更大规模的数据存储。
- 高性能:多个主节点并行处理请求,提高整体吞吐量。
- 高可用性:支持主节点的自动故障转移,每个主节点的从节点可以接管其任务。
- 负载均衡:读写请求可以分布到不同的主节点,提升整体性能。
- 复杂性增加:需要更多的配置和管理,节点之间需要协调一致。
适用场景:
- 大规模数据存储和高并发访问需求。
- 需要分布式存储和高可用性的应用。
- 需要扩展 Redis 集群以应对增长的数据量和流量。
三种模式的比较
- 主从复制模式(Master-Slave Replication)
- 优势:数据备份、读写分离、配置简单。
- 劣势:主节点故障时需要手动切换,写操作无法负载均衡,存储能力受限于单机。
- 哨兵模式(Sentinel Mode)
- 优势:基于主从复制,自动故障恢复,提高高可用性,减少人工干预。
- 劣势:写操作无法负载均衡,存储能力受限于单机,不支持数据分片。
- 集群模式(Cluster Mode)
- 优势:实现数据分片和负载均衡,突破单机存储限制,支持高并发和高性能,具备较完善的高可用方案。
- 劣势:配置和管理复杂,节点之间需要良好的协调机制。
18、Redis的脑裂
网络分区时,旧主可能仍向一部分客户端提供写服务,而 Sentinel 已在另一侧提升新主,形成两个主节点并存的窗口。分区恢复后,旧主重新成为从节点时,分叉写入不会自动合并,可能被新主的数据覆盖。
Redis 7.x / 8.0 可通过以下配置降低风险(示例,需要至少三个符合条件的从节点):
min-replicas-to-write 3
min-replicas-max-lag 10主节点统计处于 online 状态、距最近一次从节点 ACK 的时间 不超过 10 秒的从节点;这样的节点少于 3 个时拒绝写命令,通常返回 NOREPLICAS。不是任意一个从节点超时就拒写,也不是“符合条件时禁止写入”。这里的 lag 是 ACK 新鲜程度,不是主从偏移差对应的准确时间,更不是每笔业务写入已复制完成的证明。任一配置为 0 会关闭这项保护;默认 min-replicas-to-write 为 0。
风险边界: 这是一项以写可用性换取风险收敛的本地准入条件,不是多数派提交或防脑裂的 fencing 机制。ACK 尚未过期时旧主仍可能接受写入;若旧主一侧一直保有足够合格从节点,它也可能持续可写。配置不会等待当前写入被这些从节点确认或落盘,因此不能承诺零丢数,也不能消除双主。WAIT 可等待当前连接此前写入的复制确认,但不使 Redis 成为强一致系统,也不直接保证落盘;业务仍需结合客户端路由、故障切换和持久化要求设计。
依据:Redis 7.0 配置说明、Redis 8.0 ACK 统计实现及 WAIT 官方边界。
19、哨兵之间是怎么发现彼此的 ?(了解)
- 是通过PSUBSCRIBE这个Redis内置的发布订阅命令来实现的,他们共同监听 sentinel:hello 这个channel,每隔两秒钟,每个哨兵都会往自己监控的某个master+slaves对应的__sentinel__:hello channel 里发送一个消息,内容是自己的host、ip和runid还有对这个master的监控配置,每个哨兵也会去监听自己监控的每个master+slaves对应的 sentinel:hello channel ,然后去感知到同样在监听这个master+slaves的其他哨兵的存在,每个哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步。
20、如何优化Redis内存?
- 选择合适的数据类型
- 大value选择gzip压缩
- 避免使用大key
- 批处理采取Pipeline(小心配置单次pipeline命令总大小,不要撑爆socket缓冲区)
21、Redis为什么这么快?(了解)
- 纯内存操作,且时间复杂度大多都是O(1),也有特例,比如zset底层跳表是log(n),keys....等!
- 数据结构底层存储的设计比较节省空间
- 核心命令主要串行执行,减少相关锁竞争和线程切换,其他 I/O 与后台工作见第 7 题。
- 采取I/O多路复用,epoll模型,非阻塞IO。
22、五种数据类型的底层存储原理是什么?(了解,跳表需要重点看下)
逻辑数据类型与内部编码是两个层次;编码取决于版本、元素内容、规模与配置。下面限定 Redis Open Source 的五种常用类型,不能把旧版 ziplist/linkedlist 答案直接套用到 Redis 7/8。
| 类型 | Redis 7.0 | Redis 7.2 / 7.4 | Redis 8.0 |
|---|---|---|---|
| String | int、embstr、raw | 同左 | 同左 |
| List | quicklist,紧凑节点使用 listpack | 小列表可直接 listpack,较大时 quicklist | 同 Redis 7.2 |
| Hash | 小对象 listpack,较大时 hashtable | 通常同左;7.4 字段过期场景另见下文 | listpack / hashtable;字段过期可用 listpackex |
| Set | 纯整数小集合 intset,否则 hashtable | 增加小集合 listpack,仍有 intset / hashtable | 同 Redis 7.2 |
| Sorted Set | 小对象 listpack,较大时 skiplist(内部跳表 + 字典) | 同左 | 同左 |
Redis 3.2 起 List 使用 quicklist,Redis 7.0 将其紧凑节点从 ziplist 换为 listpack;Redis 7.0 的 listpack 节点不等于 List 对象直接采用 listpack 编码,后者可见于 Redis 7.2。Hash / Zset 的紧凑编码在 Redis 7.0 也由 ziplist 换为 listpack;Set 的 listpack 是 Redis 7.2 新增。
Redis 7.4 引入 Hash 字段过期;Redis 8.0 对紧凑 Hash 调用字段过期相关命令时可转换为 listpackex,额外保存过期元数据,不能把所有 Hash 一律列成只有两种编码。上表不穷举所有 Redis 8 后续小版本的内部优化。
紧凑结构节省指针和分配开销,但查找通常要线性扫描;规模或元素长度超过阈值时会转换编码。Hash / Zset 的紧凑阈值分别由 hash-max-listpack-*、zset-max-listpack-* 等参数控制,实际对象可用 OBJECT ENCODING key 查看。跳表支持有序遍历及范围查询,字典辅助按成员定位;常见成员更新为 O(log N),范围查询还要计入返回的 M 个元素,不能把所有 Zset 操作都写成 O(log N)。
依据:OBJECT ENCODING、Redis 7.0 List 实现、Redis 7.2 List 实现、Redis 8.0 Hash 实现及 Redis 8.0 编码阈值配置。
参考资料
- Redis cache-aside with Jedis:按需加载、带 TTL 回填及写后缓存失效。
- Redis persistence:RDB、AOF 同步策略、重写及多文件备份边界。
- Redis 4.0 配置示例:
aof-use-rdb-preamble对混合 AOF 的定义,区别于独立开启 RDB 快照。 - Diagnosing latency issues:后台同步、fork 和磁盘操作对延迟的影响。
- Diving into Redis 6:核心命令执行与多线程网络 I/O 的区别。
- Redis replication:异步复制及故障转移中的数据丢失风险。
本次修订(2026-11-06):校正高并发下缓存更新顺序与延时双删误区,补充一致性失败路径、RDB/AOF 恢复边界,以及线程和子进程的区别;补正 Redis 7/8 的 PSYNC、复制偏移与 backlog、min-replicas 拒写边界和编码版本差异。
需要继续练习时,可从学习资源入口选择配套材料,再把第 2 题的失败时序与第 3 题的恢复边界写进自己的项目复盘。
继续阅读与资料
继续追问:删除失败如何补偿、旧值为何会回填。
