少女祈祷中...
NEKO
知识库

redis 线程模型网络面试要点

一、线程模型基础类

Q1:Redis 是单线程还是多线程?

  • 命令执行是单线程,这是 Redis 的核心设计。

  • 但 Redis 并非完全单线程:

    • 网络 IO:Redis 6 之前单线程,6.0 后可多线程。

    • 持久化:RDB fork 子进程,AOF 重写 fork 子进程。

    • 异步删除:UNLINK、FLUSHALL ASYNC 用后台线程。

    • 集群同步:后台线程处理。

  • 所以准确说法是:主线程单线程处理命令,其他辅助功能多线程。

Q2:Redis 为什么用单线程?

  1. 避免锁竞争:单线程无并发问题,不需要加锁。

  2. 避免上下文切换:无线程切换开销。

  3. 实现简单:代码可维护性高。

  4. 瓶颈不在 CPU:Redis 瓶颈通常是内存和网络,不是 CPU。

  5. 原子性天然保证:单条命令天然原子。

Q3:单线程为什么还能高并发?

  • 纯内存操作,单次命令执行极快(微秒级)。

  • IO 多路复用,一个线程管理大量连接。

  • 非阻塞 IO,不让线程等待网络。

  • 所以单线程也能支撑数万 QPS。

Q4:单线程有什么缺点?

  • 无法利用多核 CPU:只能用一个核。

  • 阻塞命令影响全局:如 KEYS、FLUSHALL 会阻塞所有请求。

  • 大 key 操作慢:删除大集合会卡住主线程。

  • 所以生产要避免阻塞命令。

二、IO 多路复用类

Q5:什么是 IO 多路复用?

  • 一个线程同时监听多个文件描述符(fd)。

  • 有事件就绪时通知程序处理,避免为每个连接创建线程。

  • Redis 用 epoll(Linux)实现。

Q6:select、poll、epoll 的区别?

维度

select

poll

epoll

fd 上限

1024

无

无

数据结构

数组

链表

红黑树 + 就绪链表

遍历

O(n)

O(n)

O(1) 取就绪

触发方式

LT

LT

LT/ET

跨平台

好

好

Linux

  • Redis 在 Linux 用 epoll,其他平台用 kqueue、select 等。

Q7:epoll 的 LT 和 ET 模式?

  • LT(水平触发):只要 fd 可读/可写就一直通知。默认,简单。

  • ET(边缘触发):只在状态变化时通知一次,必须一次读完。

  • Redis 用 LT,简化处理。

Q8:Redis 的事件循环是什么?

  • 主线程循环处理:

    1. 处理文件事件(网络 IO)。

    2. 处理时间事件(定时任务)。

    3. 阻塞在 epoll_wait 等待事件。

  • 文件事件:读、写、连接、关闭。

  • 时间事件:过期清理、持久化触发。

Q9:Redis 怎么处理客户端连接?

  • 客户端连接后,注册到 epoll。

  • 有数据可读时,事件循环调用读处理器。

  • 解析 RESP 协议,执行命令,写回响应。

  • 写完注册写事件,可写时发送。

三、Redis 6 多线程类

Q10:Redis 6 为什么引入多线程?

  • 单线程网络 IO 成为瓶颈,尤其高并发下。

  • 多线程处理网络读写和协议解析,提升吞吐。

  • 命令执行仍单线程,保证原子性和简单性。

Q11:Redis 6 多线程具体做什么?

  • 网络 IO:多线程读 socket、解析协议、写响应。

  • 命令执行:仍由主线程单线程执行。

  • 所以是“IO 多线程 + 命令单线程”。

Q12:Redis 6 多线程怎么配置?

  • io-threads N:IO 线程数,建议 CPU 核数,最多 8。

  • io-threads-do-reads yes:读也多线程。

  • 默认关闭,io-threads 1 表示不启用。

  • 主线程也参与 IO,所以实际线程数 = io-threads。

Q13:多线程会带来并发问题吗?

  • 不会。命令执行仍单线程,无共享数据竞争。

  • 多线程只处理网络 IO,不碰数据。

  • 所以不需要加锁。

Q14:为什么命令执行不也做成多线程?

  • 会引入锁竞争、上下文切换、复杂度。

  • 原子性难以保证。

  • 单线程命令执行已足够快,瓶颈在网络 IO。

四、网络协议类

Q15:Redis 的通信协议是什么?

  • RESP(REgistered Simple Protocol)。

  • 支持:简单字符串、错误、整数、批量字符串、数组。

  • RESP3(Redis 6)新增:Map、Set、Double、Boolean 等。

Q16:RESP 协议的特点?

  • 简单、易解析、人类可读。

  • 二进制安全。

  • 请求是数组,响应按类型前缀。

  • 例:*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n

Q17:Redis 的 Pipeline 是什么?

  • 一次发送多条命令,减少 RTT。

  • 客户端打包命令,服务端依次执行,批量返回。

  • 不是原子操作,中间可能插入其他命令。

  • 注意批量大小,避免内存和阻塞。

Q18:Pipeline 和事务的区别?

维度

Pipeline

事务

原子性

无

有(MULTI/EXEC)

目的

减少 RTT

原子执行

命令间

可插入其他

不插入

错误处理

逐条

整体

Q19:Redis 的请求响应流程?

  1. 客户端发命令。

  2. 服务端 epoll 触发读事件。

  3. 解析 RESP,执行命令。

  4. 结果写入输出缓冲区。

  5. epoll 触发写事件,发送响应。

五、阻塞与性能类

Q20:哪些操作会阻塞 Redis?

  • KEYS *:遍历所有 key。

  • FLUSHALL、FLUSHDB:清库。

  • DEL 大 key:同步删除。

  • HGETALL 大 Hash。

  • SORT、ZRANGE 大集合。

  • SAVE:同步持久化。

  • Lua 脚本执行过长。

Q21:怎么避免阻塞?

  • 用 SCAN 替代 KEYS。

  • 用 UNLINK 替代 DEL 大 key。

  • 用 HSCAN、SSCAN 分批。

  • 用 BGSAVE 替代 SAVE。

  • Lua 脚本控制执行时间。

  • 大 key 拆分。

Q22:SCAN 的原理?

  • 游标遍历,每次返回部分 key。

  • 不保证返回所有 key(rehash 期间可能重复)。

  • 不阻塞,适合生产替代 KEYS。

  • 变体:HSCAN、SSCAN、ZSCAN。

Q23:Redis 的 QPS 大概多少?

  • 单机通常 10 万级 QPS,取决于命令复杂度。

  • Pipeline 可大幅提升。

  • 瓶颈常在网络和内存,不在 CPU。

Q24:怎么提升 Redis 吞吐?

  • 用 Pipeline 批量。

  • 用多线程 IO(Redis 6)。

  • 避免阻塞命令。

  • 读写分离、集群分片。

  • 合理设置连接数。

六、高频陷阱题

Q25:Redis 单线程,为什么还有并发问题?

  • 命令执行单线程,单条命令原子。

  • 但多条命令组合不是原子,如 GET 后 SET。

  • 需用事务、Lua、分布式锁保证。

Q26:Redis 6 多线程后,还需要单线程吗?

  • 命令执行仍单线程,这是核心。

  • 多线程只加速网络 IO。

  • 所以“单线程”说法仍成立(指命令执行)。

Q27:io-threads 越大越好吗?

  • 不是。建议不超过 CPU 核数,通常 4~8。

  • 太大反而上下文切换开销大。

  • 低并发时多线程无收益。

Q28:Pipeline 会阻塞 Redis 吗?

  • 单次 Pipeline 命令太多会占用内存和主线程。

  • 要控制批量大小,如每次几百条。

  • 不会阻塞其他客户端,但会延迟处理。

Q29:Redis 为什么不用多线程处理命令?

  • 锁竞争、上下文切换、复杂度高。

  • 原子性难保证。

  • 单线程已够快,瓶颈不在命令执行。

Q30:epoll 比 select 快在哪?

  • epoll 用红黑树管理 fd,O(1) 取就绪事件。

  • select 每次遍历所有 fd,O(n)。

  • epoll fd 无上限,select 有 1024 限制。

  • epoll 内核回调,无需轮询。

评论