仲裁队列以及磁盘的重要性
仲裁队列在 RabbitMQ 中相对较新,许多人尚未从经典镜像队列迁移到仲裁队列。在迁移到这种新队列类型之前,您需要确保您的硬件能够支持您的工作负载,而存储驱动器是其中的一个重要因素。
在这篇博文中,我们将更仔细地探讨仲裁队列及其在不同存储配置下的性能特征。
HDD 还是 SSD?一个驱动器还是多个驱动器?
简而言之,我们强烈建议在使用仲裁队列时使用 SSD。原因是仲裁队列对 IO 延迟敏感,而 SSD 提供的 IO 延迟比 HDD 低。IO 延迟较高会导致吞吐量较低、端到端延迟较高以及其他一些不良影响。
在本文的后面,我们将通过使用各种 SSD 和 HDD 配置的基准测试来演示我们推荐此建议的原因。
为什么配额队列对 IO 延迟敏感?
让我们来看一下单个代理的写入路径,了解为什么会这样。请记住,发布和消费都算作写入。发布涉及*入队*操作,而消费涉及*确认*操作,两者都必须持久化和复制。请注意,如果您在没有发布者确认或消费者确认的情况下使用配额队列或镜像队列,您需要问问自己为什么使用*已复制的队列*。
操作首先被写入内存和写前日志(WAL)。每个代理有一个 WAL,服务于该代理上的所有配额队列。然后,操作由段写入器(Segment Writer)写入每个队列的段文件。

但是,有一项优化措施意味着入队操作(消息)可能永远不需要写入段文件。新到达的消息保留在内存中,并且可以在消息写入段文件之前被传递给消费者并由消费者确认。在这种情况下,这些消息不会被写入磁盘,因为从代理的角度来看,它们基本上已不存在。
这意味着,对于消费者能够跟上的队列,消息通常根本不会被写入段文件。当内存中的消息准备刷新到段文件时,消费者已经确认了该消息。如果 IO 延迟很高,那么 WAL 最终会成为瓶颈。每个代理都有一个 WAL 和一个段写入器进程,它们充当配额队列 Raft 集群在其之上运行的共享基础设施。将操作 fsync 到活动 WAL 文件就像 Raft 集群的心脏在跳动。如果 fsync 缓慢,Raft 集群的吞吐量就会缓慢。
段文件的 fsync 也可能成为瓶颈。WAL 由一个或多个文件组成;一个文件正在被积极写入,零个或多个*非活动*文件因达到最大 WAL 文件大小限制而被滚动。这些非活动 WAL 文件只有在它们的所有消息都已写入段文件和/或已确认(参见上面的优化)后才能安全删除。*如果段文件的写入速度很慢,那么 WAL 会越来越大,因为消息无法足够快地写入段文件。*
HDD 擅长大型顺序写入,但不擅长小型或随机 IO。如果您的写入需要访问多个文件,在文件系统中跳转,那么 HDD 会产生比 SSD 高得多的 IO 延迟。WAL 和段文件的优点是它们是仅追加文件。因此,HDD 有可能提供不错的性能,但仅限于没有大量随机 IO 争用的情况。一旦 WAL 和段文件 IO 需要与其他磁盘 IO 竞争,性能就会开始下降。
好的,那么 HDD 的性能看起来怎么样?
现在我们将使用 SSD 和 HDD 运行一些性能基准测试并分析结果。
有四个主要使用磁盘的参与者
- Mnesia 和消息存储(数据)
- 日志(logs)
- 配额队列段文件(segment)
- 配额队列 WAL(wal)
这些工作负载中的每一个都有不同的 IO 模式,我们可以尝试隔离这些磁盘工作负载以提高性能。
我们有六个集群,它们共享某些方面,例如 CPU 数量,但使用不同类型和数量的磁盘。
共享
- 3 节点集群
- 实例类型:c4.4xlarge
- 16 vCPU
- 15GB RAM
- 2Gbps EBS 实例吞吐量(VM 的磁盘 IO 吞吐量上限为 2Gbps/250MBs)
- 5Gbps 网络(625MB/s)
所有磁盘均为 200GB SSD(io1)或 1TB HDD(st1)。它们各自的吞吐量容量都大于 c4.4xlarge EC2 实例可以使用。
独特
- 将所有数据存储在单个驱动器中
- 集群 rabbitmq1,SSD1=data/logs/segment/wal
- 集群 rabbitmq10,HDD1=data/logs/segment/wal
- 将 WAL 存储在单独的驱动器上,但段文件与经典队列数据存储在同一驱动器上
- 集群 rabbitmq4,SSD1=data/logs/segment SSD2=wal
- 集群 rabbitmq13,HDD1=data/logs/segment HDD2=wal
- 为经典队列数据、段文件和 WAL 文件分配各自专用的驱动器
- 集群 rabbitmq7,SSD1=data/logs SSD2=segment SSD3=wal
- 集群 rabbitmq16,HDD1=data/logs HDD2=segment HDD3=wal
纯配额队列工作负载
首先,我们将测试仅由配额队列组成的工作负载。
吞吐量基准测试 #1 - 一个配额队列
一个发布者,一个队列,一个消费者,1kb 消息,无速率限制。

我们看到 SSD 集群 rabbitmq1、rabbitmq4 和 rabbitmq7 都达到了大约 19k 消息/秒。HDD 集群的吞吐量较低,rabbitmq13(2 个磁盘)和 rabbitmq16(3 个磁盘)仅略微落后,约为 17k 消息/秒。单个 HDD 集群明显落后,约为 13k 消息/秒。
结论
对于单个队列工作负载,将 WAL 与段文件工作负载分离到单独的磁盘上,为我们提供了接近 SSD 的性能。*
吞吐量基准测试 #2 - 四个配额队列
4 个发布者,4 个队列,4 个消费者,1kb 消息,无速率限制。

对于 4 个配额队列,我们看到了不同的情况。HDD 的性能比其 SSD 对应物高约 2k 消息/秒。我们必须记住,当消费者能够跟上时,操作通常只写入 WAL 文件。WAL 是由四个队列共享的,所以我们通过它传输更多字节。WAL 文件是顺序写入的,我们的 st1 HDD 可以管理 500MB/s 的吞吐量(尽管 VM 本身限制为 250MB/s)。
结论
在只有顺序写入的世界里,HDD 有可能产生与 SSD 相似甚至更好的结果。一个仅包含配额队列的工作负载,在消费者能够跟上的情况下,应该会看到绝大多数写入只进入一个仅追加的 WAL 文件。
延迟基准测试 #1 - 40 个配额队列
40 个发布者,40 个队列,40 个消费者,1kb 消息,每个发布者 10 消息/秒(总共 400 消息/秒)。
SSD

HDD

对于每秒总共只有 400 条 1kb 消息的速率,我们看到 SSD 和 HDD 的端到端延迟相当。
延迟基准测试 #2 - 40 个配额队列
40 个发布者,40 个队列,40 个消费者,1kb 消息,每个发布者 50 消息/秒(总共 2000 消息/秒)。

HDD

这次我们看到 HDD 的延迟明显更高
- 75 百分位数 ~4ms vs ~15ms
- 99.9 百分位数 ~20ms vs ~110ms
我们还看到,两个磁盘和三个磁盘的 HDD 配置比单个 HDD 的延迟更低。两个和三个磁盘配置之间没有太大区别,因为没有 Mnesia 或消息存储数据与配额段数据竞争。
结论
在更高的吞吐量下,我们看到 SSD 的延迟比 HDD 低得多。
轻度混合工作负载(经典惰性队列和配额队列)
到目前为止,我们已经独立测试了配额队列,没有进行任何经典队列或镜像队列负载。在此测试中,我们将查看配额队列在配额队列和未复制的惰性队列混合工作负载中的行为。惰性队列比常规经典队列更占用磁盘,应该会产生更多的磁盘争用。
吞吐量基准测试 #1 - 一个配额队列
一个发布者,一个队列,一个消费者,1kb 消息,无速率限制。
惰性队列工作负载:40 个发布者,40 个队列,40 个消费者,16b 消息,每个发布者 10 消息/秒(400 消息/秒)。

我们看到,在这种背景下的惰性队列负载下,SSD 集群的吞吐量没有受到影响,仍然保持在 20k 消息/秒。然而,单个 HDD 集群受到了严重影响,从 13k 消息/秒下降到仅 6k 消息/秒。写入密集型 WAL 工作负载现在必须与消息存储竞争,这将涉及大量的磁盘寻道。
两个和三个磁盘的 HDD 集群表现更好,吞吐量仅略有下降,这是因为大多数写入都进入了具有专用磁盘的 WAL,并且仍然实现了顺序写入。
结论
低非配额队列消息吞吐量会更严重地影响 HDD,但可以通过将工作负载分离到不同的磁盘来缓解这种影响。
吞吐量基准测试 #2 - 四个配额队列
4 个发布者,4 个队列,4 个消费者,1kb 消息,无速率限制。
惰性队列工作负载:40 个发布者,40 个队列,40 个消费者,16b 消息,每个发布者 10 消息/秒(400 消息/秒)。

同样,我们看到了 HDD 吞吐量的相同下降,单个磁盘受到的影响最大。
延迟基准测试 #1 - 40 个配额队列
40 个发布者,40 个队列,40 个消费者,1kb 消息,每个发布者 10 消息/秒(总共 400 消息/秒)。
惰性队列工作负载:40 个发布者,40 个队列,40 个消费者,16b 消息,每个发布者 10 消息/秒(400 消息/秒)。
SSD

HDD

上次,在没有混合工作负载的情况下,我们看到 SSD 和 HDD 的端到端延迟相当。然而,这次单个 HDD 集群的端到端延迟大幅增加,75 百分位数为 50ms,99.9 百分位数为 300ms。两个和三个磁盘配置表现更好,75 百分位数的延迟相当,但我们看到 99.9 百分位数的峰值达到 25ms。
结论
在轻度混合工作负载下,单个 HDD 配置表现不佳,但具有独立 WAL 磁盘的配置表现几乎与 SSD 一样好。
延迟基准测试 #2 - 40 个配额队列
40 个发布者,40 个队列,40 个消费者,1kb 消息,每个发布者 50 消息/秒(总共 2000 消息/秒)。
惰性队列工作负载:40 个发布者,40 个队列,40 个消费者,16b 消息,每个发布者 10 消息/秒(400 消息/秒)。
SSD

HDD

同样,单个 HDD 的表现最差,但这次两个和三个磁盘的 HDD 集群表现更差,徘徊在 20-60ms 之间,而 SSD 则徘徊在 5-15ms 之间。
结论
在轻度混合工作负载但配额队列负载更高的情况下,HDD 明显处于劣势。
中度混合工作负载(经典惰性队列和配额队列)
这次我们将惰性队列流量增加五倍,从 400 消息/秒提高到 2000 消息/秒,看看我们的 SSD 和 HDD 集群表现如何。
吞吐量基准测试 #1 - 一个配额队列
一个发布者,一个队列,一个消费者,1kb 消息,无速率限制。
惰性队列工作负载:40 个发布者,40 个队列,40 个消费者,16b 消息,每个发布者 50 消息/秒(2000 消息/秒)。

这次 HDD 的吞吐量非常低。我们可以看到两个和三个磁盘的配置有帮助,但帮助不大
- 1 个磁盘:~300 消息/秒
- 2 个磁盘:~1700 消息/秒
- 3 个磁盘:~2300 消息/秒
但看看 SSD,它们的吞吐量与以往一样。
结论
一旦混合工作负载达到一定程度,HDD 上的配额队列吞吐量就会变得非常低,无论您是否隔离了磁盘工作负载。显然,这里还有另一个因素在起作用,因为具有隔离段和 WAL 文件与经典队列负载的三个磁盘配置是不够的。
吞吐量基准测试 #2 - 四个配额队列
4 个发布者,4 个队列,4 个消费者,1kb 消息,无速率限制。
惰性队列工作负载:40 个发布者,40 个队列,40 个消费者,16b 消息,每个发布者 50 消息/秒(2000 消息/秒)。

这次单个 HDD 几乎无法处理一条消息。两个和三个磁盘的配置达到了大约 2300 消息/秒。
这次请注意,单个 SSD 集群受到了惰性队列流量的影响。两个和三个 SSD 集群几乎没有受到影响,这表明配额队列即使在 SSD 上也可以从磁盘工作负载隔离中受益。
延迟基准测试 #1 - 40 个配额队列
40 个发布者,40 个队列,40 个消费者,1kb 消息,每个发布者 10 消息/秒(总共 400 消息/秒)。
惰性队列工作负载:40 个发布者,40 个队列,40 个消费者,16b 消息,每个发布者 50 消息/秒(2000 消息/秒)。
SSD

HDD

单个 HDD 基本上无法运行,而两个和三个磁盘的配置出现了很大的延迟,其中两个磁盘的延迟甚至超过了 1 秒,尽管消息速率很低。
结论
随着配额队列和非配额队列负载的增加,HDD 集群的端到端延迟持续恶化。SSD 保持基本不变。
延迟基准测试 #2 - 40 个配额队列
40 个发布者,40 个队列,40 个消费者,1kb 消息,每个发布者 50 消息/秒(总共 2000 消息/秒)。
惰性队列工作负载:40 个发布者,40 个队列,40 个消费者,16b 消息,每个发布者 50 消息/秒(2000 消息/秒)。
SSD

HDD

同样,单个 HDD 集群未能处理任何消息。三个磁盘配置(完全将配额队列负载与惰性队列磁盘负载隔离开)提供了最佳延迟,但仍然远高于 SSD。
最终结论
SSD 上的配额队列表明它们对混合工作负载不敏感,但在高负载下可以从多 SSD 驱动器配置中受益。
我们还看到,在 HDD 上运行时,配额队列在处理**混合**工作负载时表现不佳。可以通过将段文件和 WAL 文件与 Mnesia(元数据)和消息存储(经典队列数据)工作负载隔离在单独的 HDD 上来缓解这些问题,但在一定级别的经典队列流量下,吞吐量会大大降低。这个负载水平完全取决于您的具体设置。
什么情况下配额队列在 HDD 上可能是安全的?虽然不推荐,但您仍然可能在低队列数量的纯配额队列工作负载或低流量的混合工作负载中获得不错的性能。但我们已经看到,配额队列在 HDD 上的性能可能会急剧下降,所以您是在冒险。我们强烈建议使用 SSD,并劝阻在使用配额队列时使用 HDD。
在这一系列的下一篇文章中,我们将探讨如何从经典镜像队列迁移到配额队列,并使用一些示例工作负载来演示您可能会遇到的情况。