单线程的Redis性能为什么这么快

Redis采用了一种非常简单的做法,单线程来处理来自所有客户端的并发请求,Redis把任务封闭在一个线程中从而避免了线程安全问题。

至于为什么是单线程的,官方的解释是,CPU并不是Redis的瓶颈所在,Redis的瓶颈主要在机器的内存和网络的带宽。

Redis采用的是基于内存的、单进程、单线程模型的KV数据库,由C语言编写,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。这个数据不比采用单进程多线程的同样基于内存的KV数据库Memcached差!有兴趣的可以参考官方的基准程序测试《How fast is Redis?》(https://redis.io/topics/benchmarks)

Redis为什么这么快:

  1. 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
  2. 数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
  3. 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
  4. 使用多路I/O复用模型;
  5. 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

多路I/O复用

Redis是跑在单线程中的,所有的操作都是按照顺序执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以I/O操作在一般情况下往往不能直接返回,这回导致某一文件的I/O阻塞导致整个进程无法对其他客户端提供服务,而I/O多路复用就是为了解决这个问题而出现的。

在这之前,先简单了解下几种I/O模型:

  1. 同步阻塞IO(Blocking IO):传统的IO模型
  2. 同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK
  3. IO多路复用(IO Multiplexing):也叫异步阻塞IO,即经典的Reactor设计模式,java中的selector和Linux中的epoll都是这种模型
  4. 异步IO(Asynchronous IO):也叫异步非阻塞IO,即经典的Proactor设计模式

异步和同步、阻塞和非阻塞,感觉原理都差不多,来简单了解下:

  • 同步和异步,是指用户线程和内核的交互方式
  • 阻塞和非阻塞,是指用户线程调用内核IO操作的方式是阻塞还是非阻塞的