前言
作为内存中数据存储,Redis 以其速度和性能着称,通常被用作大多数后端服务的缓存解决方案。
但是,在内部,Redis 采用单线程架构。
为什么单线程设计依然会有这么高的性能?如果利用多线程并发处理请求不是更好吗?
在本文中,让我们深入探讨为什么 Redis 才有单线程架构,依然如此之快,主要从下面4个方面讲解。
- 内存数据存储
- 优良的数据结构
- 单线程架构
- 非阻塞IO
让我们一一剖析。
内存数据存储
访问 RAM 比磁盘快几个数量级
Redis 是一个基于内存存储数据,也就是上面表的RAM。
Redis 中的每个读写操作都等同于从命中 RAM(随机存取存储器)的变量中读取和写入。
访问 RAM 比直接访问磁盘快几个数量级,因此,Redis 比其他数据存储快得多。
优良的数据结构
作为内存中的数据存储,Redis 利用各种底层数据结构来高效地存储数据,而无需担心如何将它们持久化到持久存储中。
例如,Redis 列表是使用链表实现的,该链表允许在列表的头部和尾部附近进行恒定时间 O(1) 的插入和删除。
另一方面,Redis 排序集是通过跳跃列表实现的,它可以实现更快的查询和插入。
简而言之,无需担心持久化数据,Redis 中的数据可以更有效地存储,以便通过不同的数据结构进行快速检索。
单线程架构
单线程进程
Redis 的写入和读取速度极快,CPU 使用率对 Redis 来说从来都不是问题。
根据 Redis 官方文档,在普通 Linux 系统上运行时,Redis 每秒可以传递多达 100 万个请求。
然而,瓶颈主要来自网络 I/O。Redis 中的处理时间主要浪费在等待网络 I/O 上。
虽然多线程架构允许应用程序通过上下文切换并发处理任务,但 Redis 的性能提升是微乎其微的,因为大多数线程最终会在 I/O 中被阻塞。
通过采用单线程架构,Redis有下面的几个好处:
- 最小化由于线程创建或销毁引起的 CPU 消耗
- 最大限度地减少由于上下文切换引起的 CPU 消耗
- 减少锁开销,因为多线程应用程序需要锁来进行线程同步,这很容易出错
- 能够使用各种“线程不安全”命令,例如 lpush
非阻塞 I/O
为了处理传入的请求,服务器需要在套接字上调用系统调用以将数据从网络缓冲区读取到用户空间。
这通常是一个阻塞操作,线程被阻塞并且在完全接收到来自客户端的数据之前什么都不做。
为什么我们不在确定套接字中的数据已准备好读取时才调用系统调用?
这就是 I/O 多路复用发挥作用的地方。
I/O 多路复用模块同时监视多个套接字,并且只返回可读的套接字。
准备好读取的套接字被推送到单线程事件循环,并由相应的处理程序使用Reactor Pattern
进行处理。
简而言之,
- 由于其阻塞性质,网络 I/O 很慢
- Redis内存操作速度快,Redis收到命令后可以快速执行
因此,Redis 有意识地做出以下决定:
- 使用 I/O 多路复用来缓解缓慢的网络 I/O 问题
- 使用单线程架构减少锁开销
总结
综上所述,单线程架构是Redis团队经过时间考验的深思熟虑的选择。尽管是单线程的,Redis 仍然是性能最高和最常用的内存数据存储之一。