我正在尝试理解 Redis 的基础知识。但是一个一直出现的问题是,Redis 是单线程的,这使得操作原子性更好。但是我无法想象它在内部是如何工作的。我有以下疑问。
如果应用程序是IO绑定的 (像Node.js),我们不是会设计单线程服务器, 在启动IO操作后该线程就会空闲, 一旦完成IO操作并将数据返回给客户端(提供并发)。但是在Redis中,所有数据都在主存储器中可用,我们根本不需要进行IO操作。那么为什么Redis还是单线程呢?如果第一个请求花费太多时间,剩余的请求是否必须一直等待?
我正在尝试理解 Redis 的基础知识。但是一个一直出现的问题是,Redis 是单线程的,这使得操作原子性更好。但是我无法想象它在内部是如何工作的。我有以下疑问。
如果应用程序是IO绑定的 (像Node.js),我们不是会设计单线程服务器, 在启动IO操作后该线程就会空闲, 一旦完成IO操作并将数据返回给客户端(提供并发)。但是在Redis中,所有数据都在主存储器中可用,我们根本不需要进行IO操作。那么为什么Redis还是单线程呢?如果第一个请求花费太多时间,剩余的请求是否必须一直等待?
TL;DR: Redis使用单线程使其更简单,但仍受I/O限制。
内存就是I/O。Redis仍受I/O限制。当Redis承受重载并达到最大请求每秒时,通常会出现网络带宽或内存带宽不足的情况,而通常没有充分利用CPU。对于某些命令来说,这种情况可能不成立,但对于大多数用例而言,Redis仍然严重受到网络或内存的I/O限制。
除非内存和网络速度突然提高了几个数量级,否则单线程通常不是问题。如果您需要扩展到一个或几个线程(例如:主<->从<->从设置),您已经在考虑Redis集群了。在这种情况下,如果您某种方式受到CPU限制并希望最大化线程数,可以为每个CPU核心设置一个集群实例。
我对Redis源代码或内部结构不太熟悉,但我可以看出使用单线程使得实现无锁原子操作更容易。线程会使得这更加复杂,并且似乎并没有大的好处,因为Redis不受CPU限制。在Redis实例上面实现并发似乎是一个很好的解决方案,这也是Redis Sentinel和Redis Cluster的作用。
当Redis处理一个长时间的请求时,其他请求会怎样?
那些其他请求将被阻塞,直到Redis完成该长时间请求。如果需要,您可以使用client-pause
命令进行测试。
当然,正确答案是卡尔的。不过在Redis v4中,我们正在看到一种从主要单线程向选择性和谨慎多线程转变的趋势。模块和线程安全上下文就是其中的一个例子。另外两个例子是新的UNLINK
命令以及适用于FLUSHDB/FLUSHALL
的ASYNC
模式。未来的计划是将更多目前由主事件循环(例如,I/O绑定任务)执行的工作转移到工作线程。
来自Redis网站:
Redis采用大多数单线程设计。这意味着一个进程服务于所有客户端请求,使用一种称为多路复用的技术。这意味着Redis可以在每个给定时刻服务于单个请求,因此所有请求都是按顺序服务的。这与Node.js的工作方式非常相似。然而,这两个产品通常不被认为是慢的。这部分是由于完成单个请求的时间很短,但主要是因为这些产品被设计为不会在系统调用(例如从套接字读取数据或写入数据)上阻塞。
我说Redis大多数是单线程,因为实际上从Redis 2.4开始,我们在Redis中使用线程以便在后台执行一些缓慢的I/O操作,主要涉及磁盘I/O,但这并不改变Redis使用单个线程服务所有请求的事实。
内存不是I/O操作。