ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ScyllaDB 集群 Schema 更新失败(Failure to Update the Schema)排查与 Raft 手动恢复实战指南

ScyllaDB 集群 Schema 更新失败(Failure to Update the Schema)排查与 Raft 手动恢复实战指南 ScyllaDB 集群 Schema 更新失败Failure to Update the Schema排查与 Raft 手动恢复实战指南【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb导读当 ScyllaDB 集群中部分节点宕机、导致 Raft 共识组group 0失去法定多数quorum时DDL 等 schema 更新会直接失败。本文以 docs/troubleshooting/failed-update-schema.rst 为骨架结合 docs/troubleshooting/handling-node-failures.rst 的完整恢复流程以及 service/raft/raft_group0.cc、service/storage_service.cc 等源码实现讲解失败根因、按集群规模划分的处置策略以及一种在多数节点永久不可恢复时从零重建 group 0 的手动恢复流程。读完本文你将能判断自己的集群属于哪类故障场景并掌握从查询commit_idx、选择恢复领导者、滚动重启到清理旧 Raft 内部数据的完整操作命令。1. 问题根因Raft 共识与法定多数QuorumScyllaDB 将集群元数据schema、拓扑变更等交由Raft 共识算法维护。这里的 Raft 组被称为group 0它承载了 schema 表等状态机数据。Raft 的核心约束是任何写入包括 schema 更新必须由多数节点quorum确认后才能提交。因此当集群中宕机的节点数达到多数时新写入无法获得多数确认schema 更新以及拓扑变更直接失败已提交的数据读写在多数存活节点上仍可继续多数仍在线时宕机节点恢复后会先联系集群拉取最新 schema再开始服务查询。源码层面对应关系如下group 0 的持久化状态由 service/raft/raft_group0.cc 中的raft_group0管理节点重启时会通过 setup_group0_if_exist() 依据持久化的raft_group0_id恢复 Raft 服务器节点加入/重建 group 0 的核心流程在setup_group0()/join_group0()service/raft/raft_group0.cc中实现其中就包含恢复模式下以recovery_leader为首的新组发现逻辑。关于故障节点数量与集群规模的具体对应关系官方文档给出了三个典型集群的对照表见下一节。恢复动作取决于节点数、数据中心DC数以及是否有节点能够重新上线。2. 按集群规模划分的故障处置对照表handling-node-failures.rst 给出了三个典型场景的决策表判断原则是只要存活的节点仍构成多数schema 与拓扑变更就安全一旦失去多数就必须重启至少一个宕机节点夺回 quorum或进入手动恢复流程。2.1 集群 A单数据中心3 节点故障后果处置动作1 个节点宕机schema 与拓扑更新可能且安全尝试重启该节点若节点已死亡使用节点替换流程replace_node_first_boot见 replace-dead-node.rst2 个节点宕机读写仍可用schema 与拓扑变更不可用至少重启 2 个宕机节点中的 1 个以夺回 quorum若无法恢复任何 1 个参考下文手动恢复流程2.2 集群 B双数据中心6 节点每 DC 3 节点故障后果处置动作1–2 个节点宕机schema 与拓扑更新可能且安全尝试重启节点若节点已死亡执行节点替换流程3 个节点宕机读写仍可用schema 与拓扑变更不可用重启 3 个宕机节点中的 1 个以夺回 quorum若无法恢复任何 1 个参考手动恢复流程整个 1 个 DC 宕机读写仍可用schema 与拓扑变更不可用DC 恢复上线后重启节点若该 DC 永久不可恢复且节点丢失参考手动恢复流程2.3 集群 C三数据中心9 节点每 DC 3 节点故障后果处置动作1–4 个节点宕机schema 与拓扑更新可能且安全尝试重启节点若节点已死亡替换为新节点可并行替换多个见 replace-dead-node-or-more.rst1 个 DC 宕机schema 与拓扑更新可能且安全DC 恢复后尝试重启集群节点若节点已死亡在新区域添加 3 个新节点见 add-dc-to-existing-dc2 个 DC 宕机读写仍可用schema 与拓扑变更不可用DC 恢复后重启节点若至少一个 DC 永久不可恢复且节点丢失参考手动恢复流程关键结论读写可用 与 schema/拓扑可更新 是两个不同的事实。丢失多数节点后业务读写依赖的副本可能仍然可用尤其是 RF 较高时但任何元数据变更都会阻塞这是运维中最容易误判的场景。3. 手动恢复流程Manual Recovery Procedure3.1 适用条件与前置说明手动恢复流程适用于多数节点例如 3 节点中 2 个永久不可恢复的情形。其总体思路是将所有存活节点以特殊恢复模式重启让集群从零初始化一个新的 group 0这一次故障节点不再参与用标准节点替换流程替换所有故障节点退出恢复模式清理旧的 Raft 内部数据。注意此流程假设集群已启用consistent topology changes——这在 2025.2 及以后版本是强制要求。恢复期间请确认该特性确实已启用。源码中对这一流程的直接支持体现在recovery_leader配置项见 db/config.cc 与 db/config.hh注释明确指出该选项会为手动 Raft 恢复流程禁用部分护栏流程结束后务必取消设置service/storage_service.cc 中storage_service在检测到recovery_leader后打印的日志Performing Raft-based recovery procedure with recovery leader host ID/IPRaft-based recovery procedure - found group 0 with ID new group 0 IDservice/raft/raft_group0.cc恢复模式下跳过常规的discovery leader 初始化逻辑直接以恢复领导者为起点创建新 group 0。3.2 前置条件Prerequisites确认故障节点真的死了不可恢复的节点必须被彻底隔离不能只是临时网络分区。如果它们可能复活并与集群通信会干扰恢复流程并造成不可预知的问题——必要时配置防火墙规则让存活节点拒绝来自这些死节点的任何通信。确认所有存活节点处于正常状态用nodetool status检查见 status 文档。若存在 join/leave 过程中的节点它无法被恢复必须永久停止恢复完成后再次执行nodetool status若该节点仍出现在输出中说明其他节点仍视其为成员需要用节点移除流程将其移除见 remove-node.rst。评估数据丢失风险若死节点数量 ≥ 某个 keyspace 的复制因子RF部分数据已丢失需要从备份恢复。手动恢复完成后执行恢复操作见 restore 文档。决定是否停机执行ScyllaDB 在恢复期间仍可服务数据查询但若你丢失了部分数据或单节点重启会导致数据查询短暂不可用流程涉及滚动重启则应考虑停机。例如标准 RF3、CLQUORUM、双 DC 部署中一个 DC 全挂且另一 DC 挂 1 台时再滚动重启另一 DC 中的节点会临时中断数据查询直到该节点重启完成。3.3 完整操作步骤以下步骤均需在存活节点上执行使用cqlsh连接① 对存活节点执行滚动重启参照 rolling-restart 文档完成一次滚动重启确保所有存活节点处于一致、正常的运行状态。② 查询 group 0 ID在任意存活节点执行cqlsh SELECT value FROM system.scylla_local WHERE key raft_group0_id;该值后续步骤都会用到。其底层由 db/system_keyspace.cc 的get_raft_group0_id()/set_raft_group0_id()读写system.scylla_local实现。③ 选出恢复领导者recovery leader在每一个存活节点上执行cqlsh SELECT commit_idx FROM system.raft WHERE group_id group 0 ID;选出commit_idx最大的节点若有多个并列最大任选其一该节点即为recovery leader。为什么必须选commit_idx最大的节点因为恢复领导者的 group 0 持久化状态会通过 Raft 快照传输成为新 group 0 的初始状态见 docs/dev/topology-over-raft.md若选择了落后的节点作领导者较新的节点加入新 group 0 后可能收到使其 schema 版本回退的快照与已有副本数据产生不一致。测试用例 test/cluster/test_raft_recovery_entry_loss.py 第 43–50 行专门验证了由更新节点作领导者时落后节点通过快照传输追平这一行为。④ 清除发现状态与持久化 group 0 ID在每一个存活节点上执行cqlsh TRUNCATE TABLE system.discovery; cqlsh DELETE value FROM system.scylla_local WHERE key raft_group0_id;这对应 docs/dev/topology-over-raft.md 所述移除持久化的 group 0 ID 与发现状态使存活节点在下一次重启时尝试加入新的 group 0。⑤ 带recovery_leader配置的滚动重启再次对所有存活节点执行滚动重启但注意顺序与配置先重启 recovery leader——它必须先创建新 group 0其他节点才能加入源码中legacy_handshaker会向恢复领导者发送send_group0_modify_config请求以加入新组见 raft_group0.cc重启每个节点之前在其scylla.yaml中追加recovery_leader配置值为恢复领导者的Host IDrecovery_leader: recovery leader 的 Host ID每个节点重启后确认其日志中出现如下两条消息之一即表示已参与 Raft 恢复storage_service - Performing Raft-based recovery procedure with recovery leader host ID/IP address storage_service - Raft-based recovery procedure - found group 0 with ID 新的 group 0 ID完成本步后Raft 应已完全可用新 group 0 已建立。注意日志中出现的 group 0 ID 是新生成的、与前面步骤中查询到的旧 ID 不同。⑥ 替换所有死节点使用标准节点替换流程替换集群中的所有死节点见 replace-dead-node.rst。也可以选择用节点移除流程移除部分死节点见 remove-node.rst但这可能需要降低 keyspace 的 RF启用 tablets 时若移除后节点所在 DC 内任何 tablet keyspace 的 RF 无法被满足nodetool removenode会被拒绝。⑦ 移除recovery_leader配置在所有节点上从scylla.yaml中删除recovery_leader属性并向所有 ScyllaDB 进程发送SIGHUP信号使配置生效sudo kill -HUP $(pgrep -f scylla)配置项recovery_leader是LiveUpdate类型见 db/config.cc意味着它支持运行时热更新但文档明确要求在流程结束前必须取消设置因为它会禁用部分护栏长期开启有安全风险。⑧ 清理旧的 group 0 内部数据在每一个存活节点上执行使用前面查询到的旧 group 0 IDcqlsh DELETE FROM system.raft WHERE group_id group 0 ID; cqlsh DELETE FROM system.raft_snapshots WHERE group_id group 0 ID; cqlsh DELETE FROM system.raft_snapshot_config WHERE group_id group 0 ID;这一步移除了旧 group 0 的 Raft 日志、快照与快照配置属于收尾清理。测试工具 test/cluster/util.py 中的delete_discovery_state_and_group0_id与delete_raft_group_data与上述 SQL 操作一一对应可作为自动化验证的参考。4. 源码级原理补充恢复期间 Gossip 与拓扑协调器如何工作了解恢复流程背后的两个机制有助于在异常日志出现时快速定位问题1Gossip 对双 group 0 的容忍恢复期间节点可能暂时属于两个不同的 group 0。正常情况下节点会在gossip_digest_syn中携带自己的 group 0 ID接收方发现 ID 不一致会拒绝消息。恢复期间节点额外在gossip_digest_syn中携带本地的recovery_leader值只要发送方或接收方任意一方持有非空的recovery_leader就忽略 group 0 ID 不一致从而保证恢复过程中 Gossip 协议仍然工作见 docs/dev/topology-over-raft.md实现位于 gms/gossiper.cc 与 idl/gossip_digest.idl.hh。2拓扑协调器自动收尾新 group 0 建立后恢复领导者会照常启动拓扑协调器topology coordinator协程。该协程的设计目标是不论集群处于何种状态已开始的拓扑操作都不会永远挂起成功或回滚。因此若多数派丢失恰巧发生在某次拓扑操作进行中新协调器会接手并完成该操作通常会因死节点上的global_token_metadata_barrier失败而回滚并通过ignore_dead_nodes/ignore_dead_nodes_for_replace选项保证后续移除/替换死节点的请求能够成功见 docs/dev/topology-over-raft.md。5. 经验总结与验证建议快速判断schema 更新失败不等于数据丢失。先nodetool status统计存活节点再对照第 2 节的表格判断是否失去 quorum。夺回 quorum 优先只要还能重启宕机节点优先恢复节点而非直接做手动恢复手动恢复只应在多数节点永久死亡时使用。恢复领导者必须是commit_idx最大的节点这是避免 schema 回退、保证状态机一致性的关键仓库专门为此编写了回归测试test_raft_recovery_entry_loss.py。流程结束后务必清理删除recovery_leader配置并发送SIGHUP再删除旧 group 0 的三张内部表数据防止旧元数据干扰后续运行。自动化参考仓库的恢复相关测试test_raft_recovery_entry_loss.py、test_raft_recovery_user_data.py、test_raft_recovery_during_join.py演示了从构造多数派丢失到完成恢复的完整验证路径可作为演练恢复流程的脚本化模板。【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表