Skip to content

RabbitMQ的持久化机制介绍

RabbitMQ 通过其持久化机制来确保在节点宕机或重启等异常情况下消息不会丢失,从而极大地提高了消息传递的可靠性。 持久化并非单一设置,而是涉及交换机(Exchange)、队列(Queue)和消息(Message)三个层面的协同工作。

持久化的核心要素

要实现消息的真正持久化,必须同时满足以下三个条件:

  1. 交换机持久化 (Exchange Durability)

    • 说明:在声明交换机时,需要将其 durable 属性设置为 true。 这意味着交换机的元数据(名称、类型、绑定等)将被存储在磁盘上。如果交换机不持久化,那么当 RabbitMQ 服务重启后,该交换机会被清除,所有相关的绑定关系也会丢失。
    • 配置:在声明交换机时,将 durable 参数设为 true
  2. 队列持久化 (Queue Durability)

    • 说明:与交换机类似,队列的 durable 属性也必须设置为 true。 持久化的队列其元数据会被保存在磁盘上,确保服务重启后队列依然存在。 如果将持久化的消息发送到非持久化的队列,当服务重启时,队列会消失,其中的消息也会随之丢失。
    • 配置:在声明队列时,将 durable 参数设为 true
  3. 消息持久化 (Message Persistence)

    • 说明:消息的持久化是通过设置消息属性 delivery_mode2 (persistent) 来实现的。 只有被标记为持久化的消息,RabbitMQ 才会尽力将其写入磁盘。 如果未设置,消息默认为非持久化(delivery_mode = 1null),在服务重启后会丢失,即使它位于一个持久化队列中。
    • 配置:在发送消息时,设置其 deliveryMode 属性为 2

消息落盘时机

RabbitMQ 的消息存储机制在逻辑上分为队列索引 (queue_index)消息存储 (msg_store) 两个部分。 消息何时被写入磁盘,取决于它是否是持久化消息:

  • 持久化消息:当一条持久化消息到达队列后,它会立即被写入磁盘。 为了提高性能,通常也会在内存中保留一份消息的备份。当内存压力较大时,内存中的这份备份可能会被清除,但磁盘上的数据依然存在。

  • 非持久化消息:非持久化消息通常只存储在内存中,以获得更高的性能。只有当 RabbitMQ 服务面临内存压力时,才会将这些非持久化消息换页(写入)到磁盘中,以释放内存空间。 这种情况下写入磁盘的目的是临时存放,服务重启后这些消息依然会丢失。

此外,RabbitMQ 的写入操作也存在一些优化机制,例如: * 缓冲区:数据在写入文件前,会先进入一个缓冲区。当缓冲区满,或者达到固定的刷盘时间间隔(例如 25ms),才会将数据刷入磁盘。 * 小消息优化:对于序列化后体积很小的消息(默认小于 4096 字节),RabbitMQ 会将其直接存储在队列索引文件中,以减少写入操作,优化性能。

持久化数据的清除机制

持久化到磁盘的消息并不会永久保留,它们会在特定条件下被清除:

  1. 消费者确认 (Acknowledgement, ACK)

    • 这是最主要的清除方式。当消费者成功处理一条消息后,会向 RabbitMQ 发送一个 ACK 确认。一旦 RabbitMQ 收到该确认,就会将这条消息标记为等待删除
  2. 垃圾回收机制

    • 消息被 ACK 后,RabbitMQ 只是在逻辑上将其标记为“可删除”,并不会立即从磁盘文件中抹去。 RabbitMQ 内部有垃圾回收机制,当磁盘文件中被标记为“垃圾数据”的比例超过一定阈值(默认为 50%)时,会触发合并操作,将多个文件中的有效数据整理到新文件中,并删除旧的、只包含垃圾数据的文件,从而释放磁盘空间。
  3. 其他清除场景

    • 消息过期 (TTL):可以为队列或消息设置存活时间 (Time-To-Live)。一旦消息过期,它就会被视为“死亡”,并从队列中移除。如果配置了死信交换机,过期消息会被路由到那里。
    • 队列被删除
      • 自动删除 (Auto-delete):如果队列被标记为 auto-delete,那么当最后一个消费者取消订阅后,该队列会被自动删除,其中的所有消息(无论是否持久化)都会被清除。
      • 排他队列 (Exclusive):排他队列仅对首次声明它的连接可见,并在连接关闭时自动删除,其中的消息也会一并被清除。
      • 手动删除:通过管理工具或代码手动删除一个队列时,队列中的所有消息都会被清除。