为什么Redis是单线程(事件驱动)的?

12

我正在尝试理解 Redis 的基础知识。但是一个一直出现的问题是,Redis 是单线程的,这使得操作原子性更好。但是我无法想象它在内部是如何工作的。我有以下疑问。

如果应用程序是IO绑定的 (像Node.js),我们不是会设计单线程服务器, 在启动IO操作后该线程就会空闲, 一旦完成IO操作并将数据返回给客户端(提供并发)。但是在Redis中,所有数据都在主存储器中可用,我们根本不需要进行IO操作。那么为什么Redis还是单线程呢?如果第一个请求花费太多时间,剩余的请求是否必须一直等待?


这个答案解释得非常清楚。 - Kevin Christopher Henry
3个回答

26

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收到两个请求Req1和Req2,其中Req1是IO绑定的(需要很长时间),然后Req2到来,而Req2需要很少的IO,那么Req2将立即被处理。但是,如果Req1是CPU密集型的(在Redis中搜索某个大列表),则Req2必须等待Req1完成后才能被处理。 - Nishat
@卡尔 很棒也很正确 - 请看一下我的补充(或许是迎来新时代的序幕?) - Itamar Haber
@Nishat,你说得对,但这在未来的版本中可能会改变。 - Itamar Haber
抱歉打扰了。根据我的经验,大量的“keys”操作很容易导致CPU负载过高。无论是集群还是单机,建议尽可能使用扫描操作,正如文档中所述。 - Sergey Benner

20

当然,正确答案是卡尔的。不过在Redis v4中,我们正在看到一种从主要单线程向选择性和谨慎多线程转变的趋势。模块和线程安全上下文就是其中的一个例子。另外两个例子是新的UNLINK命令以及适用于FLUSHDB/FLUSHALLASYNC模式。未来的计划是将更多目前由主事件循环(例如,I/O绑定任务)执行的工作转移到工作线程。


1
换句话说,一个非常长时间运行的请求可以允许其他请求在同一时间运行。 - Not_a_Golfer

1

来自Redis网站:

Redis采用大多数单线程设计。这意味着一个进程服务于所有客户端请求,使用一种称为多路复用的技术。这意味着Redis可以在每个给定时刻服务于单个请求,因此所有请求都是按顺序服务的。这与Node.js的工作方式非常相似。然而,这两个产品通常不被认为是慢的。这部分是由于完成单个请求的时间很短,但主要是因为这些产品被设计为不会在系统调用(例如从套接字读取数据或写入数据)上阻塞。

我说Redis大多数是单线程,因为实际上从Redis 2.4开始,我们在Redis中使用线程以便在后台执行一些缓慢的I/O操作,主要涉及磁盘I/O,但这并不改变Redis使用单个线程服务所有请求的事实。

内存不是I/O操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接