新闻详情

新闻详情

首页 / 资讯中心 / 详情

Apache Cassandra 事务性集群元数据(TCM)实现剖析:从启动发现到一致性查询的完整链路

发布时间:2026/9/25 3:30:57来源:尧图网络
Apache Cassandra 事务性集群元数据(TCM)实现剖析:从启动发现到一致性查询的完整链路
数据库分布式数据库后端【免费下载链接】cassandraMirror of Apache Cassandra项目地址https://gitcode.com/gh_mirrors/cassandr/cassandra点击查看免费下载导读本文基于 Apache Cassandra 仓库中的设计文档 TCM_implementation.md 与其姊妹文档 TransactionalClusterMetadata.md深入剖析 Cassandra 5.x 引入的事务性集群元数据Transactional Cluster Metadata简称 TCM的内部实现。TCM 将原先依赖 Gossip 最终一致传播的元数据节点目录、Schema、数据放置等收敛到一份由 Cluster Metadata ServiceCMS线性化维护、全集群单调递增 Epoch 标识的全局日志中。读完本文你将掌握节点如何通过发现Discovery、投票Vote、注册Register进入 TCM 集群RemoteProcessor与PaxosBackedProcessor如何配合完成一次元数据提交PrepareJoin/InProgressSequence如何支撑安全的 bootstrap以及读写在遇到元数据分歧时如何借助CoordinatorBehindException与日志回补FetchPeerLog/FetchCMSLog保持一致性。TCM 的总体架构与核心概念在传统 Cassandra 中ring 拓扑、Token 分布等元数据通过 Gossip 传播属于最终一致的异步视图而在 TCM 中集群元数据被建模为一份不可变的ClusterMetadata对象其变更必须通过全局日志线性化。相关概览可参见 TransactionalClusterMetadata.mdClusterMetadata不可变的数据对象承载集群的完整元数据状态包含节点目录Directory、分布式 SchemaDistributedSchema、数据放置DataPlacements、节点的状态与数据归属ownership等信息。每次提交都会产生一个新的ClusterMetadata实例。Epoch单调递增的计数器唯一标识ClusterMetadata的每一个版本。在 Epoch.java 中可以看到Epoch.FIRST new Epoch(1)、Epoch.EMPTY new Epoch(0)这两个特殊常量——EMPTY表示尚无元数据Gossip 模式起点FIRST是分布式日志的第一条真实记录。CMSCluster Metadata Service集群中一部分节点组成的服务负责把元数据变更事件线性化地插入元数据变更日志。CMS 成员身份是动态的可以在节点退役或替换时更新参见 ClusterMetadataService.java 中的State枚举LOCAL、REMOTE、GOSSIP、RESET。Transformation副作用自由的纯函数将一个ClusterMetadata实例映射到下一个ClusterMetadata实例CMS 收到客户端提交的元数据变更事件后根据当前元数据状态决定该事件应被执行还是被拒绝。每个节点独立地按严格顺序应用复制来的 transformation产生一致的元数据视图应用完成后新的ClusterMetadata会被原子地发布因此消费元数据的代码永远不会看到半更新状态每个版本都可通过其 Epoch 唯一识别。Startup发现Discovery与投票VoteTCM 的启动流程与旧版大部分逻辑集中在StorageService不同被拆分到多个类中其核心入口是 Startup.java 的Startup#initialize。首先会初始化ClusterMetadataService然后根据种子节点seeds确定节点启动模式启动模式含义FIRST_CMS新集群的第一个 CMS 节点NORMAL作为普通非 CMS节点启动直接加入已存在的 TCM 集群VOTE常规情况下普通节点的启动模式先尝试发现已有 CMS找不到则参与投票选举出新的 CMSUPGRADE从 Gossip 模式的旧集群升级过渡到 TCMBOOT_WITH_CLUSTERMETADATA从外部元数据文件恢复启动对应TCM_UNSAFE_BOOT_WITH_CLUSTERMETADATA系统属性在常见的VOTE模式下节点把自己初始化为非 CMS 节点尝试发现一个已存在的 CMS 服务若失败则与其他被发现的参与者一起投票建立新的 CMS。如果种子节点配置正确节点会从种子处获知现有 CMS 节点的信息并尝试联系它们拉取初始日志initial log。节点随后继续启动流程最终进入StorageService#initServer在其中与 CMS 节点进行 Gossip 以获取用于故障检测Failure DetectionFD的集群新鲜视图然后等待 Gossip 稳定——同样是为了 FD 目的。Registration通过 Transformation 提交注册在加入 ring 之前节点必须先注册以获取NodeId这一步发生在Register#maybeRegister见 transformations/Register.java。注册通过ClusterMetadataService#commit提交一个Registertransformation 完成。关键点在于Register以及其它 transformation 都是无副作用side-effect free的函数负责把一份不可变的ClusterMetadata映射到下一份。由于执行注册的节点不是 CMS 节点它无法在本地执行提交必须借助RemoteProcessor这是一个简单的 RPC 工具序列化 transformation 后向发现的 CMS 节点发送TCM_COMMIT_REQ请求对应 Verb.java 中的TCM_COMMIT_REQ802 号 verb。从 ClusterMetadataService.java 的commit方法可以看到完整流程先log.waitForHighestConsecutive()重放所有在途条目再通过SwitchableProcessor根据 CMS 状态LOCAL/REMOTE/GOSSIP选择本地处理器、远程处理器或 Gossip 处理器执行提交成功后调用awaitAtLeast(result.success().epoch)等待本地日志追赶到新 Epoch。Commit RequestPaxosBackedProcessor 与分布式日志当 CMS 节点收到提交请求后会反序列化 transformation 并用PaxosBackedProcessor见 PaxosBackedProcessor.java尝试执行。其核心机制整个集群元数据日志存储在system_cluster_metadata.distributed_metadata_log表中对应DistributedMetadataLogKeyspace。执行一个简单的 CAS LWTcompare-and-swap 轻量级事务尝试以严格与最后一个 Epoch 连续的新 Epoch 向日志追加新条目。Epoch本质上就是ClusterMetadata版本的单调递增计数器。在重试管理上RemoteProcessor与 Paxos-backed 处理器都使用 Retry.java 中的Retry类远程处理器用cms_await_timeout设定其重试截止时间默认 120 秒见 Config.java 中的cms_await_timeout 120000ms、cms_default_max_retries 10、cms_default_retry_backoff 50ms。CMS 本地处理器则允许最多使用cms_rpc_timeout作为重试预算。PaxosBackedProcessor随后尝试执行Transformation执行结果只有两种Success或RejectReject 不会被持久化到日志中而是通过一次确认 transformation 是在最高 Epoch 上执行的的读操作来线性化。典型的 Reject 包括校验错误、执行 transformation 时抛出的异常等。例如Register在注册表中已存在相同 IP 地址的节点时就会返回拒绝。Commit ResponseEntry 与 LocalLog 的追平PaxosBackedProcessor成功将条目提交到分布式日志后会用Replicator把包含新追加 transformation 的Entry广播给集群其余节点——Replicator会遍历目录Directory中所有节点通知它们新的 Epoch见 ClusterMetadataService.java 的Commit.DefaultReplicator。需要强调的是这次复制不需要可靠、没有重试。如果某个节点在 CMS 尝试复制期间宕机它恢复上线后必然会通过后续机制获知新 Epoch。除了已提交的EntryCMS 返回给提交方节点的响应中还包含所有让该节点完全追赶到该提交所生效 Epoch 的条目。当RemoteProcessor收到 CMS 节点的响应后会把所有收到的条目追加到LocalLog见 log/LocalLog.java。LocalLog处理待处理条目的积压并通过构造新的ClusterMetadata来生效enact新 Epoch。BootstrapInProgressSequence、PrepareJoin 与 BootstrapAndJoin此时节点已准备好开始加入 ring 的过程起点是Startup#startup。Startup#getInitialTransformation判断节点应走常规 bootstrap而非 replace随后节点提交PrepareJointransformation见 transformations/PrepareJoin.java。在PrepareJoin期间ClusterMetadata会经历以下变化锁定将被 bootstrap 影响的 Range见 sequences/LockedRanges.java如果计算出的锁定 range 与此前已锁定的 range 相交则PrepareJoin被拒绝。计算并添加InProgressSequence见 sequences/InProgressSequences.java其中包含三个 transformationStartJoin、MidJoin、FinishJoin如果当前节点已存在与之关联的在途序列则PrepareJoin被拒绝。计算AffectedRanges在序列执行过程中放置关系将要发生变化的 range并作为提交成功消息的一部分返回。InProgressSequence随后被逐步执行节点在步骤之间需要完成的本地操作都作为 in-progress 序列的一部分实现见 sequences/BootstrapAndJoin.java 的executeNext。设计上对节点的存活不做任何假设——例如节点可能在执行PrepareJoin之后、更新本地 keyspace 中的 token 之前崩溃。因此唯一的假设是SystemKeyspace.updateLocalTokens必须在StartJoin提交之前被调用被拥有的数据必须在节点成为读 quorum 一部分之前流向该节点——即使节点在 streaming 期间崩溃或被任意次重启也必须如此。为了保证 quorum 一致性在执行每一步之前节点必须等待ProgressBarrier见 sequences/ProgressBarrier.java。CEP-21 详细解释了为什么需要 progress barrier本文只需说明AffectedRanges的大多数 owner 必须在下一步执行前获知生效了上一步的 Epoch——这是为了在最终一致查询中保持复制因子replication factor不被破坏。执行完 in-progress 序列的所有步骤后range 被解锁序列本身从ClusterMetadata中移除。QueryingCoordinatorBehindException 与日志回补当节点开始参与读写时其 ring 或 schema 视图可能与其它节点产生分歧。TCM 尽力缩小这一时间窗口但分布式系统中至少存在一些不可避免的延迟。TCM 的解决方式在节点协调coordinating的每个请求中携带该节点已知的最高 Epoch在作为副本replica响应协调者时同样携带该 Epoch。副本可以通过比较协调者的 Epoch 与schema 最后一次被修改的 Epoch给定 range 的放置最后一次被修改的 Epoch来检查当前请求的 schema 与 ring 一致性如果副本发现协调者不可能知道当前 schema 或 ring就抛出CoordinatorBehindException见 exceptions/CoordinatorBehindException.java。其它情况协调者或副本知道更高 Epoch但该 Epoch 的存在不影响当前查询的一致性或结果下落后的参与者会异步发送TCM_FETCH_PEER_LOG_REQ尝试从 peer 追平失败后再尝试用TCM_FETCH_CMS_LOG_REQ从 CMS 节点追平。这两个 verb 都定义在 Verb.java 中TCM_FETCH_PEER_LOG_REQ819 号由FetchPeerLog.Handler处理TCM_FETCH_CMS_LOG_REQ804 号由fetchLogRequestHandler处理追平逻辑的入口则在 ClusterMetadataService.java 的fetchLogFromPeerOrCMS——先尝试从指定 peer 拉取若仍落后再回退到fetchLogFromCMS阻塞式拉取并等待所有条目生效。协调者收集到足够响应后会把自己当前的 Epoch 与构造查询所用ReplicaPlan时的 Epoch 进行比较如果二者不同则检查已收集的副本响应是否仍然满足该查询执行时的一致性级别——这保证了在请求在途期间元数据发生变化时协调者不会向客户端返回违反一致性级别的结果。从 Gossip 模式升级到 TCMnodetool cms 命令TCM 引入后旧集群需要经过显式的迁移过程详见 TransactionalClusterMetadata.md 的 Upgrading and Transitioning to TCM 一节升级后的节点进入最小修改模式允许的元数据修改被限制为仅增删节点与替换节点以便在升级期间替换故障主机此模式下 CMS 没有成员每个 peer 各自独立维护自己的ClusterMetadata启动时从系统表初始化并靠 Gossip 传播允许的元数据修改子集。当操作者准备好后手动选择一个节点提升为初始 CMS使用命令nodetool cms initialize候选节点会提名自己为初始 CMS 并尝试获得集群其余节点的共识成功后验证所有 peer 拥有完全一致的ClusterMetadata视图并用该元数据的快照初始化分布式日志。该过程完成后所有未来的集群元数据更新都经由 CMS 和全局日志执行不支持回退到旧的元数据管理方式。此后应使用nodetool cms reconfigure增加更多 CMS 成员参见 ClusterMetadataService.java 的reconfigureCMS其内部提交PrepareCMSReconfiguration.Complextransformation 并等待ReconfigureCMS.SequenceKey对应的在途序列完成。小结TCM 的一致性保障闭环纵观全流程TCM 用一份不可变的ClusterMetadata加一条全局线性化日志取代了 Gossip 驱动的元数据传播提交路径非 CMS 节点通过RemoteProcessor发起TCM_COMMIT_REQCMS 节点用PaxosBackedProcessor以 CAS LWT 追加严格连续 Epoch 的条目成功后再用Replicator尽力广播、失败不回补宕机节点恢复后自行追平生效路径每个节点把收到的条目追加进LocalLog按序重放并原子发布新的ClusterMetadata安全变更路径bootstrap/扩容等操作通过PrepareJoinInProgressSequenceStartJoin/MidJoin/FinishJoinProgressBarrier分阶段线性化保证 streaming 与读 quorum 的正确时序一致性兜底路径请求携带 Epoch副本用CoordinatorBehindException拒绝落后协调者落后节点用TCM_FETCH_PEER_LOG_REQ/TCM_FETCH_CMS_LOG_REQ异步追平日志。对实现细节感兴趣的同学可继续在仓库中阅读 ClusterMetadata.java、log/LocalLog.java、ownership/DataPlacements.java 以及 log/SystemKeyspaceStorage.java并结合 test/distributed 下的分布式测试与 test/unit 中的单元测试验证各阶段的时序与失败场景。赞分享数据库分布式数据库后端【免费下载链接】cassandraMirror of Apache Cassandra项目地址https://gitcode.com/gh_mirrors/cassandr/cassandra点击查看免费下载相关推荐Apache Cassandra 事务性集群元数据TCM实现剖析从节点启动到一致读写的完整流程Apache Cassandra 事务性集群元数据TCM实现剖析从节点启动到一致读写的完整流程 本文基于 Apache Cassandra 仓库中的 TC数据库分布式数据库大数据后端Apache Cassandra 事务型集群元数据TCM基于全局日志的集群状态管理与一致性保证Apache Cassandra 事务型集群元数据TCM基于全局日志的集群状态管理与一致性保证 导读 Transactional Cluster Meta数据库分布式数据库大数据后端Apache Cassandra一致性模型深度剖析最终一致性的实现原理与实践指南Apache Cassandra一致性模型深度剖析最终一致性的实现原理与实践指南 Apache Cassandra是一个高度可扩展的分布式NoSQL数据库其数据库分布式数据库KV存储上一篇Clang ASTMatcher高级应用clang-tutor中的模式匹配技巧下一篇Minecraft模组开发终极指南HMCL测试环境快速搭建教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

EasyWeChat PHP SDK 快速上手:环境要求、安装与公众号服务端实战 2026/9/25 4:10:30

EasyWeChat PHP SDK 快速上手:环境要求、安装与公众号服务端实战

后端即时通讯 【免费下载链接】easywechat 📦 一个 PHP 微信 SDK 项目地址: https://gitcode.com/gh_mirrors/ea/easywechat 点击查看 免费下载 EasyWeChat 是一个开源的 PHP 微信开发 SDK,由开源 SaaS 平台提供商微擎(w7.cc&…

阅读更多 →
RocketRide frame_grabber 视频节点实战:三种选帧模式、PNG 帧输出与时间戳表格构建 2026/9/25 4:10:30

RocketRide frame_grabber 视频节点实战:三种选帧模式、PNG 帧输出与时间戳表格构建

【免费下载链接】rocketride-server High-performance AI pipeline engine with a C core and 50 Python-extensible nodes. Build, debug, and scale LLM workflows with 13 model providers, 8 vector databases, and agent orchestration, all from your IDE. Includes VS C…

阅读更多 →
Havoc Teamserver 配置体系解析:HCL 配置语言工具包与 yaotl Profile 的完整实现 2026/9/25 4:10:23

Havoc Teamserver 配置体系解析:HCL 配置语言工具包与 yaotl Profile 的完整实现

网络安全 【免费下载链接】Havoc The Havoc Framework 项目地址: https://gitcode.com/gh_mirrors/ha/Havoc 点击查看 免费下载 HCL(HashiCorp Configuration Language)工具包是 Havoc Teamserver 的 profile 配置文件(.yaotl&am…

阅读更多 →
随机波浪速度与波浪力计算:从JONSWAP谱到Morison方程的工程实现 2026/9/25 4:10:23

随机波浪速度与波浪力计算:从JONSWAP谱到Morison方程的工程实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
urql retryExchange 深度解析:重试机制、退避算法与版本演进全指南 2026/9/25 4:10:23

urql retryExchange 深度解析:重试机制、退避算法与版本演进全指南

前端 【免费下载链接】urql The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow. 项目地址: https://gitcode.com/gh_mirrors/ur/urql 点击查看 免费下载 urql/exchange-retry 是 urql Gr…

阅读更多 →
Yii 2 控制台应用实战指南:从内置命令到自定义 Command 的完整开发手册 2026/9/25 4:10:23

Yii 2 控制台应用实战指南:从内置命令到自定义 Command 的完整开发手册

后端Web框架 【免费下载链接】yii2 Yii 2: The Fast, Secure and Professional PHP Framework 项目地址: https://gitcode.com/gh_mirrors/yi/yii2 点击查看 免费下载 Yii 2 在提供完善的 Web 开发能力之外,还内置了与 Web 应用同等成熟的控制台&#x…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉