跳到主要内容
版本: 4.1

持久性配置

概述

本指南涵盖了一些可配置的值,这些值会影响节点的吞吐量、延迟和 I/O 特性。在得出任何结论之前,请考虑阅读整个指南并熟悉 使用 PerfTest 进行基准测试

一些相关指南包括

RabbitMQ 持久性概述

现代 RabbitMQ 版本提供了几种队列类型和流

  • 仲裁队列:复制的、持久的、面向数据安全的
  • :一种复制的、持久的数据结构,支持不同的操作(与队列不同)
  • 经典队列:原始队列类型,从 RabbitMQ 4.0 开始仅为单副本

这些队列类型具有不同的存储实现,可以调整的适用配置设置也不同。

流使用基于日志的存储机制,并在内存中保留非常少量的数据(主要是尚未写入磁盘的操作数据)。尽管如此,当客户端使用 RabbitMQ 流协议 时,它们仍能提供出色的吞吐量。

由于流是磁盘 I/O 密集型的,因此它们的吞吐量会随着消息大小的增加而降低。它们从现代 SSD 和 NVMe 存储中获益匪浅。

流不提供与存储相关的可调存储参数。

仲裁队列

仲裁队列使用由 RabbitMQ 的 Raft 实现实现的基于日志的存储机制。它们在内存中保留非常少量的数据(主要是尚未写入磁盘的操作数据)。

由于仲裁队列在执行任何操作之前都会将所有数据持久化到磁盘,因此建议使用尽可能快的磁盘。

由于仲裁队列的磁盘 I/O 密集特性,它们的吞吐量会随着消息大小的增加而降低。

可以影响仲裁队列资源使用的主要存储相关设置是预写日志段大小限制,即 WAL 内存表移动到磁盘的限制。换句话说,在稳定负载下,每个仲裁队列都能够将最多此数量的消息数据保存在内存中。

可以控制此限制

# Flush current WAL file to a segment file on disk once it reaches 32 MiB in size
raft.wal_max_size_bytes = 32000000
重要提示

由于 运行时 无法保证立即释放内存,我们建议为 RabbitMQ 节点分配至少 3 倍于有效 WAL 文件大小限制的内存。在高吞吐量系统中,将需要更多内存。对于这些系统,4 倍是一个不错的起点。

经典队列

经典队列有两种可用的存储实现:v1(原始版本)和 v2(在 RabbitMQ 3.10 及更高版本中可用)。

队列版本

RabbitMQ 3.10.0 起,broker 有了一个新的经典队列实现,名为 版本 2。版本 2 队列具有新的索引文件格式和实现,以及新的每个队列的存储文件格式,以替换直接在索引中嵌入消息的方式。

版本 2 的主要改进是在高内存压力下提高了稳定性。

RabbitMQ 3.10.0 中,版本 1 仍然是默认版本。可以在版本 1 和版本 2 之间来回切换。

可以使用 queue-version 策略 键来更改版本。通过策略设置新版本时,队列将立即转换其磁盘上的数据。可以升级到版本 2 或降级到版本 1。请注意,对于大型队列,转换可能需要一些时间,并且在转换运行时队列将不可用。

可以通过在 rabbitmq.conf 中设置 classic_queue.default_version 在配置中设置默认版本

# makes classic queues use a more efficient message storage
# and queue index implementations
classic_queue.default_version = 2

经典队列 v1 持久性概述

首先,一些背景知识:持久消息和瞬态消息都可以写入磁盘。持久消息将在到达队列后立即写入磁盘,而瞬态消息仅在内存压力下为了可以从内存中驱逐而写入磁盘。持久消息在可能的情况下也会保留在内存中,并且仅在内存压力下才会被从内存中驱逐。“持久层”是指用于将这两种类型的消息存储到磁盘的机制。

在本页中,我们使用“队列”来指代非复制队列或队列领导者或队列镜像。队列镜像是一个“高于”持久性的“层”。

持久层有两个组件:队列索引消息存储。队列索引负责维护关于给定消息在队列中的位置的知识,以及它是否已被传递和确认。因此,每个队列都有一个队列索引。

消息存储是消息的键值存储,在每个 vhost 中的所有队列之间共享。消息(消息体,以及任何元数据字段:属性和/或标头)可以直接存储在队列索引中,也可以写入消息存储。技术上有两个消息存储(一个用于瞬态消息,一个用于持久消息),但它们通常被一起视为“消息存储”。

内存成本

在内存压力下,持久层会尝试尽可能多地写入磁盘,并尽可能多地从内存中删除。但是,有些东西必须保留在内存中

  • 每个队列为每个未确认的消息维护一些元数据。如果消息的目标是消息存储,则消息本身可以从内存中删除。
  • 消息存储需要一个索引。默认消息存储索引为存储中的每条消息使用少量内存。

队列索引中的消息嵌入

将消息写入队列索引有利有弊。

此功能具有优点和缺点。主要优点是

  • 消息可以一次操作写入磁盘,而不是两次;对于小型消息,这可能是一个显著的优势。
  • 写入队列索引的消息不需要在消息存储索引中有一个条目,因此在分页输出时没有内存成本。

缺点是

  • 队列索引在内存中保留固定数量记录的块;如果将非小型消息写入队列索引,则内存使用量可能很大。
  • 如果消息通过交换器路由到多个队列,则该消息将需要写入多个队列索引。如果此类消息写入消息存储,则只需要写入一个副本。
  • 目标是队列索引的未确认消息始终保留在内存中。
  • 当使用 版本 2 时,仍然需要两次写入。

目的是将非常小的消息存储在队列索引中作为优化,并将所有其他消息写入消息存储。这由配置项 queue_index_embed_msgs_below 控制。默认情况下,序列化大小小于 4096 字节的消息(包括属性和标头)存储在队列索引中。

当从磁盘读取消息时,每个队列索引都需要在内存中保留至少一个段文件。段文件包含 16,384 条消息的记录。因此,如果增加 queue_index_embed_msgs_below,请务必谨慎;少量增加可能会导致大量内存使用。

影响操作系统的和运行时限制

持久性可能会因持久化程序在文件句柄或异步线程数量方面受到限制而表现不佳。在这两种情况下,当您有大量需要同时访问磁盘的队列时,都可能发生这种情况。

文件句柄过少

RabbitMQ 服务器可以打开的 文件句柄数量受到限制。每个正在运行的网络连接都需要一个文件句柄,其余的句柄可供队列使用。如果在考虑网络连接后,磁盘访问队列多于文件句柄,则磁盘访问队列将在它们之间共享文件句柄;每个队列可以使用文件句柄一段时间,然后将其取回并交给另一个队列。

这可以防止服务器因磁盘访问队列过多而崩溃,但可能会变得很昂贵。管理插件可以显示集群中每个节点的 I/O 统计信息;除了显示读取、写入、寻道等速率外,它还会显示文件句柄抖动率——文件句柄以这种方式回收的速率。文件句柄过少的繁忙服务器可能每秒执行数百次重新打开操作 - 在这种情况下,如果给予更多文件句柄,其性能可能会显著提高。

经典队列 v1:备用消息存储索引实现

如上所述,写入消息存储的每条消息都会为其索引条目使用少量内存。经典队列 v1 使用的消息存储索引在 RabbitMQ 中是可插拔的,并且其他实现作为插件可用,这些插件可以消除此限制。

它们未与 RabbitMQ 发行版一起发布的原因是它们都使用本机代码。请注意,此类插件通常会使消息存储运行速度变慢。

© . All rights reserved.