上一篇讲到集群共享元数据,但没说元数据存在哪。这一篇补上——RabbitMQ 的元数据存储,正在经历从 Mnesia 到 Khepri 的重要演进。这场演进和镜像队列的废弃(第 15 篇)是同一个大主题的一部分:RabbitMQ 在把整个系统的「一致性根基」从异步复制迁到 Raft 共识

理解这场演进,能解释几个现象:为什么旧版 RabbitMQ 在网络分区时元数据会不一致、为什么 4.0 启动慢(Khepri 初始化)、为什么未来版本会彻底移除 Mnesia。这一篇把来龙去脉讲清楚。

Mnesia:Erlang 自带的分布式数据库

RabbitMQ 从诞生起,元数据就存在 Mnesia 里。Mnesia 是 Erlang/OTP 自带的分布式数据库,特点:

  • Erlang 原生:和 Erlang 进程模型深度集成,访问快。
  • 支持分布式事务:能在多个节点间同步数据。
  • 可持久化:disk 节点的 Mnesia 数据写盘,ram 节点放内存。

RabbitMQ 把所有元数据——用户、vhost、权限、Exchange 定义、Queue 定义、Binding、运行时参数——都存在 Mnesia 的表里。集群里各节点的 Mnesia 表相互同步,保持元数据一致。

Mnesia 服务了 RabbitMQ 十几年,但它有一个根本性的问题:它的分布式一致性模型在网络分区下不可靠

Mnesia 的分区问题

Mnesia 的分布式同步,本质是基于 Erlang 的分布式通信和事务。它没有用共识算法,而是依赖「Erlang 节点间的网络可达性」来判断节点状态。这在网络稳定时工作良好,但网络分区时会出问题:

  • 网络分区把集群分成两半,两半各自认为对方「挂了」(实际只是网络不通)。
  • 每一半都可能各自修改元数据(比如各自创建同名的队列、各自的权限变更)。
  • 分区恢复时,两边的元数据不一致,Mnesia 要选一个版本保留、另一个丢弃。
  • 这个「选哪个」的过程不可靠——可能丢掉一部分元数据变更。

这就是和镜像队列脑裂同源的问题:Mnesia 和镜像队列都依赖「网络可达性」判断节点状态,而不是用共识算法判定「多数派」。所以 RabbitMQ 在网络分区下的两大隐患——消息丢失(镜像队列)和元数据不一致(Mnesia)——其实是同一个病根。

Khepri:基于 Raft 的元数据存储

为了根治这个问题,RabbitMQ 团队开发了 Khepri——一个基于 Raft 共识算法的元数据存储。它的核心思路和 Quorum Queue 一致:用 Raft 替代异步复制,从根本上保证一致性

Khepri 的特点:

  • 基于 Raft:元数据变更要经多数节点确认才算提交,不存在「确认了却丢失」或「分区后脑裂」的问题。
  • 复用 Quorum Queue 的 Ra 库:RabbitMQ 已经为 Quorum Queue 实现了 Raft(Ra 库),Khepri 直接复用这套成熟的共识实现,不重新造轮子。
  • 强一致:任何时刻元数据都是一致的状态,网络分区时少数派无法修改元数据。

这和镜像队列 → Quorum Queue 的演进是完全平行的:都是把「异步复制的、分区不可靠的」组件,换成「Raft 强一致的」组件。消息复制用 Quorum,元数据用 Khepri,整个系统的一致性根基统一到了 Raft 上——这场切换从 4.0 开始铺开,分几个版本落地。

演进的时间线

这场演进是渐进式的,不是一步到位:

  • RabbitMQ 3.13:Khepri 作为可选预览引入。默认仍用 Mnesia,但可以手动启用 Khepri 测试。官方鼓励用户在 3.13 上测试 Khepri,为迁移做准备。
  • RabbitMQ 4.0:Khepri 进入**正式支持(GA)**阶段,镜像队列在这一版被移除。但元数据仍默认 Mnesia,Khepri 可在新部署显式启用。
  • RabbitMQ 4.2:Khepri 成为新部署的默认元数据存储,存量集群可按计划迁移。
  • RabbitMQ 4.3:Khepri 成为唯一支持的元数据存储,Mnesia 被移除。

这条时间线的意义:Khepri 替代 Mnesia 是一个跨 4.0/4.2/4.3 的渐进过程,不是在某个版本一刀切。3.13 之前,Mnesia 的分区问题要靠运维手段(网络分区处理策略)缓解;4.2 之后 Khepri 才从架构层面成为默认解。维护 RabbitMQ 时要对照自己所在版本判断元数据层的实际状态,而不是默认它「已经切到 Khepri」。

Khepri 带来的变化与影响

Khepri 替代 Mnesia,带来几个实际影响:

更强的分区容错。 网络分区时,Khepri 保证元数据强一致——少数派无法修改元数据,避免了 Mnesia 的不一致问题。这是最大的收益。

元数据操作的语义变化。 Khepri 下,元数据变更(声明队列、绑定)是 Raft 提交,要等多数节点确认。这意味着元数据操作的延迟略增(相比 Mnesia 的本地写),但换来一致性保证。对正常业务几乎无感,因为元数据操作不频繁。

运维变化。 一些基于 Mnesia 的运维命令(如 rabbitmqctl 的某些元数据操作)在 Khepri 下行为不同。升级时要注意命令兼容性。

升级路径。 从 3.x 升到 4.x,元数据是否迁到 Khepri取决于目标小版本和部署选择:4.0 可保持 Mnesia,4.2 新部署默认 Khepri,4.3 起唯一支持 Khepri。官方提供了迁移工具和指引,迁移期间集群要停服或滚动迁移,要规划好窗口。

为什么不一开始就用 Raft

一个自然的疑问:既然 Raft 这么好,RabbitMQ 为什么不一开始就用?这涉及历史和技术演进:

  • RabbitMQ 诞生于 2007 年,那时 Raft 还没出现(Raft 论文 2014 年)。Mnesia 是当时 Erlang 生态里现成的、成熟的分布式存储,用它合理。
  • Raft 的工业级实现(如 etcd 用的、RabbitMQ 的 Ra 库)是后来才成熟的。在它成熟前,自研共识的复杂度和风险都很高。
  • 工程上,RabbitMQ 团队是先用 Quorum Queue(消息复制)验证了 Raft 在 RabbitMQ 场景下可行,再把同一套 Ra 库用于 Khepri(元数据),逐步推进,降低风险。

这是一次「认知和技术都到位后,逐步偿还历史债」的演进。也是为什么这场演进花了几个大版本——它要保证向后兼容、提供迁移路径、不破坏存量系统。

Mnesia 到 Khepri 的演进时间线

收束:统一到 Raft 是 RabbitMQ 的架构成熟标志

从镜像队列到 Quorum、从 Mnesia 到 Khepri,RabbitMQ 在 4.x 系列开启了「全系统 Raft 化」的架构升级。消息复制(Quorum)在 4.0 随镜像队列移除而成为唯一的高可用方案;元数据(Khepri)从 4.0 GA、4.2 默认,到 4.3 成为唯一存储,分几步落地。这让它从「依赖 Erlang 生态传统方案、分区下不可靠」的系统,变成「共识算法保证一致性」的现代化系统。理解这条主线,才能看懂 4.x 为什么是 RabbitMQ 的分水岭。

下一篇讲网络分区这个主题——既然一致性靠 Raft 保证了,那分区时系统具体表现如何、该怎么处理。


关于十三Tech

我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。我相信 AI 是程序员的最佳搭档。想跟完这套「图解 RabbitMQ」,欢迎关注公众号 「十三Tech」

十三Tech公众号二维码