Redis 最重要的一个特点就是它的单线程架构,很多开发与运维技巧都是基于这一点考虑的。本文主要介绍一下为什么 Redis 使用单线程可以达到 10 万 QPS。
为什么要多线程?
一般情况下,多线程的性能要高于单线程,但是这一点是基于特殊场景的。
单线程的问题在于:a. 遇到线程阻塞(等待资源,IO 阻塞等)的时候,CPU 会空闲;b. 无法利用 CPU 的多个核心。
而多线程解决了这两点,但是带来的问题是,线程共享资源的访问需要加锁,线程切换带来额外开销。
题外话:经验往往是特定场景或者大多数场景下的总结,我们要知其然,更要知其所以然。
下面来分析一下 Redis 为什么选用单线程,及为什么性能很高。
Redis 为什么选用单线程
-
Redis 是内存数据库。Redis 的主要操作都是内存操作,不会被磁盘 IO 阻塞。即使是持久化数据的时候,也是 fork 一个进程去处理,不会阻塞业务线程。
-
Redis 的网络 IO 模型采用 NIO,并且使用 Linux 下高性能的 epoll 实现,使得线程基本不会阻塞在网络 IO 上。
-
Redis 单线程避免了线程同步,线程切换带来的开销。
综上所述,Redis 在设计上避免了单线程阻塞带来的性能问题。对于单线程无法使用 CPU 多核的问题,可以启动多个 Redis 实例来解决,没有存数据的情况下,一个 Redis 实例的内存消耗不足 1M。
在大多数场景下,CPU 算力都不是性能瓶颈处。为了不让 CPU 空闲下来,前辈们使用来很多技巧来提高性能:指令重排,指令流水线,分支预测,多层缓存,页面缓存等等。
Redis 实现合理
-
Redis 使用 C 语言实现,性能高。
-
Redis 为五种基本类型都设计了两到三种数据类型,用来在合适的场景选用。Redis 的数据结构选用合理。
-
Redis 的设计专注于自己的领域,保证自己的简洁性,从而保证命令执行的高效。比如 Redis 的事务和 lua 脚本都无法保证关系型数据库那种事务原子性(Redis 的原子性指的是命令的原子性,也就是说,在这一连串命令执行的过程中,不会有其他命令插入进来执行)。Redis 的发布订阅模式无法消费历史数据等等。
最后,由于 Redis 采用了单线程架构,所以我们在使用的时候一定要注意这一点,避免执行耗时长的命令,避免阻塞业务线程。