为什么 Redis 单线程可以达到 10 万 QPS

2,147 阅读2分钟

Redis 最重要的一个特点就是它的单线程架构,很多开发与运维技巧都是基于这一点考虑的。本文主要介绍一下为什么 Redis 使用单线程可以达到 10 万 QPS。

为什么要多线程?

一般情况下,多线程的性能要高于单线程,但是这一点是基于特殊场景的。

单线程的问题在于:a. 遇到线程阻塞(等待资源,IO 阻塞等)的时候,CPU 会空闲;b. 无法利用 CPU 的多个核心。

而多线程解决了这两点,但是带来的问题是,线程共享资源的访问需要加锁,线程切换带来额外开销。

题外话:经验往往是特定场景或者大多数场景下的总结,我们要知其然,更要知其所以然。

下面来分析一下 Redis 为什么选用单线程,及为什么性能很高。

Redis 为什么选用单线程

  1. Redis 是内存数据库。Redis 的主要操作都是内存操作,不会被磁盘 IO 阻塞。即使是持久化数据的时候,也是 fork 一个进程去处理,不会阻塞业务线程。

  2. Redis 的网络 IO 模型采用 NIO,并且使用 Linux 下高性能的 epoll 实现,使得线程基本不会阻塞在网络 IO 上。

  3. Redis 单线程避免了线程同步,线程切换带来的开销。

综上所述,Redis 在设计上避免了单线程阻塞带来的性能问题。对于单线程无法使用 CPU 多核的问题,可以启动多个 Redis 实例来解决,没有存数据的情况下,一个 Redis 实例的内存消耗不足 1M。

在大多数场景下,CPU 算力都不是性能瓶颈处。为了不让 CPU 空闲下来,前辈们使用来很多技巧来提高性能:指令重排,指令流水线,分支预测,多层缓存,页面缓存等等。

Redis 实现合理

  1. Redis 使用 C 语言实现,性能高。

  2. Redis 为五种基本类型都设计了两到三种数据类型,用来在合适的场景选用。Redis 的数据结构选用合理。

  3. Redis 的设计专注于自己的领域,保证自己的简洁性,从而保证命令执行的高效。比如 Redis 的事务和 lua 脚本都无法保证关系型数据库那种事务原子性(Redis 的原子性指的是命令的原子性,也就是说,在这一连串命令执行的过程中,不会有其他命令插入进来执行)。Redis 的发布订阅模式无法消费历史数据等等。

最后,由于 Redis 采用了单线程架构,所以我们在使用的时候一定要注意这一点,避免执行耗时长的命令,避免阻塞业务线程。