Redis为什么这么快

Redis快的原因

  1. 内存结构
  2. 单线程
  3. IO多路复用

内存结构

Redis是KV结构的内存数据库,在内存上操作数据,而非磁盘。

单线程

Redis为什么是单线程的?

It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.
However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

因为单线程已经够用了,CPU 不是 redis 的瓶颈。Redis 的瓶颈最有可能是机器内存 或者网络带宽。既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章地采用单线程的方案了。
这里的单线程指的只是在处理我们的网络请求的时候只有一个线程来处理,一个正式的Redis Server运行的时候肯定是不止一个线程的。而且我们使用单线程的方式是无法发挥多核CPU 性能,不过我们可以通过在单机开多个Redis 实例来完善!

单线程为什么这么快?

首先要讲一下内存模型:
计算机主存(内存)可看作一个由 M 个连续的字节大小的单元组成的数组,每个字 节有一个唯一的地址,这个地址叫做物理地址(PA)。早期的计算机中,如果 CPU 需要内存,使用物理寻址,直接访问主存储器。


直接操作内存

这种方式有几个弊端:

  1. 在多用户多任务操作系统中,所有的进程共享主存,如果每个进程都独占一块物理地址空间,主存很快就会被用完。我们希望在不同的时刻,不同的进程可以共用同一块物理地址空间。
  2. 如果所有进程都是直接访问物理内存,那么一个进程就可以修改其他进程的内存数据,导致物理地址空间被破坏,程序运行就会出现异常。

为了解决这些问题,就出现了在 CPU 和主存之间增加一个中间层。CPU 不再使用物理地址访问,而是访问一个虚拟地址,由这个中间层把地址转换成物理地址, 最终获得数据。这个中间层就是虚拟内存。


虚拟内存

在每一个进程开始创建的时候,都会分配一段虚拟地址,然后通过虚拟地址和物理
地址的映射来获取真实数据,这样进程就不会直接接触到物理地址,甚至不知道自己调用的哪块物理地址的数据。

进程切换

多任务操作系统是怎么实现运行远大于 CPU 数量的任务个数的?当然,这些任务实际上并不是真的在同时运行,而是因为系统通过时间片分片算法,在很短的时间内,将 CPU 轮流分配给它们,造成多任务同时运行的错觉。为了控制进程的执行,内核必须有能力挂起正在 CPU 上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。

在每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,也就是 说,需要系统事先帮它设置好 CPU 寄存器和程序计数器(ProgramCounter),这个叫做 CPU 的上下文。
而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加 载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。
在切换上下文的时候,需要完成一系列的工作,这是一个很消耗资源的操作。

单线程的好处

  1. 没有创建线程、销毁线程带来的消耗。
  2. 避免了上线文切换导致的 CPU 消耗。
  3. 避免了线程之间带来的竞争问题,例如加锁释放锁死锁等等。

IO多路复用

传统IO模型

以读操作为例:当应用程序执行 read 系统调用读取文件描述符(FD)的时候,如果这块数据已经存在于用户进程的页内存中,就直接从内存中读取数据。如果数据不存在,则先将数据从磁盘加载数据到内核缓冲区中,再从内核缓冲区拷贝到用户进程的页内存中。(两次拷贝,两次 user 和 kernel 的上下文切换)。


BlockingIO


当使用 read 或 write 对某个文件描述符进行过读写时,如果当前 FD 不可读,系统就不会对其他的操作做出响应。从设备复制数据到内核缓冲区是阻塞的,从内核缓冲区 拷贝到用户空间,也是阻塞的,直到 copy complete,内核返回结果,用户进程才解除 block 的状态。

IO多路复用

I/O 指的是网络 I/O。
多路指的是多个 TCP 连接(Socket 或 Channel)。 复用指的是复用一个或多个线程。 它的基本原理就是不再由应用程序自己监视连接,而是由内核替应用程序监视文件
描述符。
客户端在操作的时候,会产生具有不同事件类型的 socket。在服务端,I/O 多路复 用程序(I/O Multiplexing Module)会把消息放入队列中,然后通过文件事件分派器(File event Dispatcher),转发到不同的事件处理器中。



多路复用有很多的实现,以 select 为例,当用户进程调用了多路复用器,进程会被 阻塞。内核会监视多路复用器负责的所有 socket,当任何一个 socket 的数据准备好了, 多路复用器就会返回。这时候用户进程再调用 read 操作,把数据从内核缓冲区拷贝到用户空间。

I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符, 而这些文件描述符(套接字描述符)其中的任意一个进入读就绪(readable)状态,select() 函数就可以返回。

采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。

你可能感兴趣的:(Redis为什么这么快)