单线程设计的考虑

Redis采用单线程好处在于避免了多线程对数据竞争的问题,加锁的问题,上下文切换的问题。据官方解释,redis的瓶颈不在cpu,而在内存或者网络的带宽,综合考虑然后就采用了单线程。(Redis的性能非常高,每秒可以承受10W+的QPS,因为大部分操作在内存中,采用的IO多路复用机制)这里说的单线程是指处理网络请求时只是用一个线程,redis本身在持久化的时候还是会用到额外的线程的。

到了Redis4.0开始也支持了多线程,当然只是针对部分命令采用的是多线程,例如:UNLINK(对删除操作懒处理)、FLUSHALL (清理数据库)、ASYNC(异步化)等。引入这些的目的是:在某些情况下,尽可能的提升效率,假设有一个key大到几十M,这时DEL这个key的时候,可能会短暂的阻塞,这时如果用unlink来删除,刚开始只是删除这个key,真正的value是后台线程去删除的。

内存操作和IO多路复用机制

Redis 是基于内存的数据库,本身数据就存在于内存里,这个过程不会受到磁盘 I/O 的限制。

IO多路复用的意思就是多个网路IO即为多个TCP连接复用一个进程或者线程,这种模型最大的好处就是不用为每个连接创建一个进程或者线程。比较经典的模型就是 select、poll、epoll

select:select(fds),一次性把fds交给内核,然后内核告诉哪些fd可读可写(内核自己遍历,而不用用户遍历,将多次的系统调用变成1次系统调用)。fds最大是1024,这也决定了select模型最大并发是1024。

poll:和select差不多,只不过并发不止1024了,可以更多

epoll: select和poll的缺点是内核遍历的时间复杂度是O(n),虽然用户不用遍历了,减少了陷入内核的次数,但是内核还是要遍历的。epoll的优点就是内核也不需要遍历,当用户把fds传给内核时,会依赖硬件中断,比如当网卡有数据到来时,就会中断告诉cpu,cpu就知道哪个fd有数据到达了。

Redis采用了非阻塞的IO多路复用(redis默认采用epoll,除非系统不支持)技术。redis本身就是一个事件驱动程序,redis把socket抽象成文件事件。这里说的IO多路复用就是文件事件处理器以单线程的方式,来监听相关的套接字,应对大量的请求,Redis 中使用 I/O 多路复用程序同时监听多个套接字,并将这些事件推送到一个队列里,然后逐个被执行。最终将结果返回给客户端(accept、read、write、close)。由于IO多路复用程序是一个单线程,那么当多个socket到来时,肯定要排队,它们总是以队列的方式顺序地处理。

无法使用多核CPU。Redis作者提到,由于Redis的大部分操作并不是CPU密集型任务,而Redis的瓶颈在于内存和网络带宽。在高并发请求下,Redis需要更多的内存和更高的网络带宽,否则瓶颈很容易出现在内存不够用和网络延迟等待的情况。

        单个的Redis是单线程,采用了线程封闭的方式,把任务封闭在一个线程,避免了线程安全问题,不过对于需要依赖多个 Redis 操作(即多个 Redis 操作命令)的复合操作来说,依然需要锁,而且有可能是分布式锁。