1. Redis 是单线程还是多线程?#
分析
核心处理逻辑,Redis 一直都是单线程的,其它辅助模块也会有一些多线程、多进程的功能,比如:
- 复制模块用的多进程;
- 某些异步流程从 4.0 开始用的多线程,例如 UNLINK、FLUSHALL ASYNC、FLUSHDB ASYNC 等非阻塞的删除操作;
- 网络 I/O 解包从 6.0 开始用的是多线程。
但是这种分支模块,都只是辅助,最核心的还是处理架构,这块 Redis 始终是单线程的。
回答
Redis 核心处理是单线程的,在 6.0 中使用了多线程进行 I/O 解包、回包。其他一些边缘点的,比如使用多线程进行删除数据等异步任务,这个倒是 4.0 就引入了。
2. Redis 为什么选择单线程做核心处理?#
分析
为什么用单线程,潜台词其实对应的是为什么不用多线程。
很多同学只说多线程有多大成本、多复杂,这样就容易被追问多线程这么多问题,那为什么 MySQL 等很多组件都是用多线程呢,总有优势吧?为啥 Redis 不利用这种优势?
所以这里需要从投入产出比分析,我们先看产出:
首先 Redis 的定位,是内存 k-v 存储,是做短平快的热点数据处理,一般来说执行会很快,执行本身不应该成为瓶颈,而瓶颈通常在网络 I/O,处理逻辑多线程并不会有太大收益。
再看投入:
引入多线程带来极大的复杂度,比如原来的顺序执行特性就不复存在,为了支持事务的原子性、隔离性,Redis 就不得不引入一些很复杂的实现,多线程模式也使得程序调试更加复杂和麻烦,会带来额外的开发成本及运营成本,也更容易犯错。同时,也带来上下文切换成本、同步机制的开销、线程本身也占据内存大小等投入成本。
回答
我们从投入产出来看。首先如果引入多线程,主要是希望充分利用多核的性能,但 Redis 的定位,是内存 k-v 存储,是做短平快的热点数据处理,一般来说执行会很快,执行本身不应该成为瓶颈,而瓶颈通常在网络 I/O,处理逻辑多线程并不会有太大收益。同时,支持多线程的话,我们需要付出更大的复杂度、以及多线程上下文切换、同步机制的开销等成本。这样综合来看,成本高且收益不大,所以最终选择了不做,事实也证明,单线程的 Redis 也确实足够高效。
3. Redis 单线程性能如何?#
分析
这个主要考察直观的认知,Redis 实际表现为单机(普通 8 核 16G 的机器)读 10 多万,写几万,非常炸裂。可以说自己也用过 redis-benchmark 来测试过。
命令:
redis-benchmark -h 127.0.0.1 -p 6379 -t set,get -n 10000 -q
结果:
SET: 108695.65 requests per second
GET: 149253.73 requests per second
回答
Redis 单线程的性能是很好的,在普通机器每秒能 10 多万的读性能、几万的写性能。我在我自己的 mac 上也有用 redis-benchmark 来测试过,写性能高达 11 万,读性能高达 15 万。
4. 为什么单线程还能这么快?#
分析
这里很多同学都能答到内存存储、高效的数据结构,但是遗漏 I/O 多路复用,其实某种意义上 I/O 复用是非常核心的,因为 Redis 使用单线程的核心,是瓶颈在 I/O 而不是 CPU,那 I/O 复用正好能对症下药。
- 基于内存操作:Redis 的绝大部分操作在内存里就可以实现,数据也存在内存中,与传统的磁盘文件操作相比减少了 IO,提高了操作的速度;
- 高效的数据结构:Redis 有专门设计了 STRING、LIST、HASH 等高效的数据结构,依赖各种数据结构提升了读写的效率;
- 采用单线程:单线程操作省去了上下文切换带来的开销和 CPU 的消耗,同时不存在资源竞争,避免了死锁现象的发生;
- I/O 多路复用:采用 I/O 多路复用机制同时监听多个 Socket,根据 Socket 上的事件来选择对应的事件处理器进行处理。
回答
我认为有三点,一个是内存存储,这是 Redis 的定位也是快的前提,一个是高效的数据结构,Redis 的数据结构可以说是追求极致,持续调优,最后一个是 I/O 多路复用,Redis 的瓶颈在 I/O 而不是 CPU,那 I/O 复用正好能对症下药。
5. Redis 6.0 之后引入了多线程,你知道为什么吗?#
分析
其实大多数面试官,并不知道哪个版本引入了啥东西,除了 6.0 引入多线程,因为这个事情噱头很大,而且是动了核心处理逻辑的一部分。
要回答这个问题,就要回归到 Redis 瓶颈所在:是 I/O 而不是 CPU,但是随着互联网发展,请求量巨大的时候单线程在进行同步读写 I/O 的时间,单核 CPU 有时候也是不够用了。
回答
Redis 主要瓶颈是 I/O 而不是 CPU,但随着互联网的高速发展,在部分高并发场景,单核 CPU 也不见得处理得过来了,所以针对核心处理流程中的解包、发包这两个 CPU 耗时操作,进行了多线程优化,充分发挥多核优势。
6. Redis 6.0 的多线程是默认开启的吗?#
分析
这个主要是想看你是否真的了解过 6.0 多线程。
如果能答出对默认关闭的看法,可能会加分。
回答
默认是关闭的,如果想要开启需要用户在 redis.conf 配置文件中修改。默认关闭一方面是为了兼容以前的,毕竟很多用户的认知中,Redis 是单线程的,第二可能也是认为多线程并不是必要的,在大多数场景不开启也是完全够用的。
7. Redis 6.0 的多线程主要负责命令执行的哪一块?#
分析
Redis 主要瓶颈是 I/O 而不是 CPU,但随着互联网的高速发展,在部分高并发场景,单核 CPU 也不见得处理得过来了,所以针对核心处理流程中的解包、发包这两个 CPU 耗时操作,进行了多线程优化,充分发挥多核优势。
回答
原来核心流程中的 I/O 处理,包括解包和回包,也就是读写客户端 socket 的 I/O,这两部分都消耗 CPU 时间,多线程的引入主要也是为了解决单核 CPU 在大数据下还是不够用的问题。