Redis 为什么用单线程
Redis单线程指的什么
Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。 但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执 行的。
多线程提高系统吞吐率
日常写程序时,我们经常会听到一种说法:“使用多线程,可以增加系统吞吐率,或是可 以增加系统扩展性。”的确,对于一个多线程的系统来说,在有合理的资源分配的情况 下,可以增加系统中处理请求操作的资源实体,进而提升系统能够同时处理的请求数,即 吞吐率。下面的左图是我们采用多线程时所期待的结果。
但是,请你注意,通常情况下,在我们采用多线程后,如果没有良好的系统设计,实际得 到的结果,其实是右图所展示的那样。我们刚开始增加线程数时,系统吞吐率会增加,但 是,再进一步增加线程时,系统吞吐率就增长迟缓了,有时甚至还会出现下降的情况。
多线程共享资源
为什么会出现这种情况呢?一个关键的瓶颈在于,系统中通常会存在被多线程同时访问的 共享资源,比如一个共享的数据结构。当有多个线程要修改这个共享资源时,为了保证共 享资源的正确性,就需要有额外的机制进行保证,而这个额外的机制,就会带来额外的开 销。
举个栗子
拿 Redis 来说,Redis 有 List 的数据类型,并提供出队(LPOP) 和入队(LPUSH)操作。假设 Redis 采用多线程设计,如下图所示,现在有两个线程 A 和 B,线程 A 对一个 List 做 LPUSH 操作,并对队列长度加 1。同时,线程 B 对该 List 执行 LPOP 操作,并对队列长度减 1。为了保证队列长度的正确性,Redis 需要让线程 A 和 B 的 LPUSH 和 LPOP 串行执行,这样一来,Redis 可以无误地记录它们对 List 长度的修 改。否则,我们可能就会得到错误的长度结果。这就是多线程编程模式面临的共享资源的 并发访问控制问题。
并发访问控制一直是多线程开发中的一个难点问题,如果没有精细的设计,比如说,只是 简单地采用一个粗粒度互斥锁,就会出现不理想的结果:即使增加了线程,大部分线程也 在等待获取访问共享资源的互斥锁,并行变串行,系统吞吐率并没有随着线程的增加而增 加。甚至减少,因为多线程会存在多线程频繁切换开销
采用多线程开发一般会引入同步原语来保护共享资源的并发访问,这也会降低系统 代码的易调试性和可维护性。为了避免这些问题,Redis 直接采用了单线程模式。
Redis 使用单线程小结
使用多线程,可以增加系统吞吐率,如果没有良好的系统设计,实际得 到的结果我们刚开始增加线程数时,系统吞吐率会增加,但 是,再进一步增加线程时,系统吞吐率就增长迟缓了,有时甚至还会出现下降的情况
出现上述情况的原因是redis在使用多线程时同样会存在多线程同时访问的 共享资源的问题,为了保证共 享资源的正确性,就需要有额外的机制进行保证。
在没有良好的系统设计,这个额外的机制,就会带来额外的开 销。这种开销有时不但无法增加系统吞吐率,反而会降低系统吞吐率
多线程开发一般会引入同步原语来保护共享资源的并发访问,这也会降低系统 代码的易调试性和可维护性