ScyllaDB Nodetool rebuild 命令完全指南:跨数据中心数据重建与 RBNO 修复机制
【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb
rebuild是 ScyllaDB 中一条重要的节点运维命令,用于通过流式传输(streaming)从集群中其他节点重建本节点的数据,其行为与 bootstrap(加入集群时的数据初始化)类似。本指南以官方文档 docs/operating-scylla/nodetool-commands/rebuild.rst 为骨架,结合仓库源码深入解析其工作流程、--force与source-dc参数语义、RBNO(Repair Based Node Operations)模式下的修复式重建实现,以及 vnode 与 tablet keyspace 的适用性差异。读完本文,你将掌握nodetool rebuild的正确使用场景、参数含义与底层原理,能够安全地在新数据中心接入、本地数据丢失恢复等场景中执行该命令。
命令概述
nodetool rebuild的完整语法为:
nodetool rebuild [[--force] <source-dc-name>]它通过从集群中其他节点流式传输数据来重建一个节点的数据,工作方式与 bootstrap 相似。从 tools/scylla-nodetool.cc 的命令注册可以看到,rebuild 支持两个参数:
--force:即使使用 source_dc 选项不安全,也强制使用该选项执行 rebuild;source-dc(位置参数,默认 "any DC"):指定从哪个数据中心流式传输数据。
scylla-nodetool在收到命令后会构造 REST API 请求,将参数原样传给 ScyllaDB 的 HTTP 接口:
void rebuild_operation(scylla_rest_client& client, const bpo::variables_map& vm) { std::unordered_map<sstring, sstring> params; if (vm.contains("source-dc")) { params["source_dc"] = vm["source-dc"].as<sstring>(); } if (vm.contains("force")) { params["force"] = "true"; } client.post("/storage_service/rebuild", std::move(params)); }对应的服务端入口位于 api/storage_service.cc,它调用ss.local().rebuild(std::move(source_dc))进入 service/storage_service.cc 中的核心实现。
注意:本仓库的 nodetool 实现基于 ScyllaDB 自带的
scylla-nodetool(tools/scylla-nodetool.cc),命令最终通过 REST API(POST /storage_service/rebuild)而不是 JMX 触发,这是 ScyllaDB 与 Apache Cassandra 在工具实现上的一个差异点。
执行流程:ScyllaDB 如何找到数据源
当执行该命令时,ScyllaDB 会按以下步骤工作:
- 确定本节点负责的 token 范围(ranges):ScyllaDB 首先计算待重建节点(本地节点)当前负责的 token 范围;
- 寻找包含相同范围的节点:确定集群中哪些节点持有这些相同 token 范围的数据副本;
- 流式传输数据:从数据源节点将对应范围的数据流式传输到本地节点。
在非 RBNO 模式下,重建每个 token range 时,数据是从**单个源副本(single source replica)**流式传输的。
从源码看,storage_service::rebuild()首先检查是否存在 tablet 启用的 keyspace(见下文“与 tablet keyspace 的关系”一节),然后调用raft_rebuild():
future<> storage_service::rebuild(utils::optional_param source_dc) { return run_with_api_lock(sstring("rebuild"), [source_dc] (storage_service& ss) -> future<> { if (auto tablets_keyspaces = ss._db.local().get_tablets_keyspaces(); !tablets_keyspaces.empty()) { std::ranges::sort(tablets_keyspaces); slogger.warn("Rebuild is not supported for the following tablets-enabled keyspaces: {}: ...", tablets_keyspaces); } co_await ss.raft_rebuild(source_dc); }); }在raft_rebuild()(service/storage_service.cc)中,ScyllaDB 会通过 group0/raft 向拓扑状态机提交一个topology_request::rebuild请求,并在请求中携带rebuild_option(即source_dc,若带--force则会以"source_dc:force"的形式编码):
sstring source_dc = sdc_param.value_or(""); if (sdc_param.force() && !source_dc.empty()) { source_dc += ":force"; } builder.with_node(raft_server.id()) .set("topology_request", topology_request::rebuild) .set("rebuild_option", source_dc) .set("request_id", guard.new_group0_state_id());同时raft_rebuild()还包含几个重要的前置校验:
- 本地节点必须已是集群成员(否则抛出 “local node is not a member of the cluster”);
- 本地节点状态必须是
normal; - 本地节点必须拥有 token(无 token 时仅记录警告并跳过冗余 rebuild);
- 集群中至少要有 2 个 normal 节点(否则抛出 “Cannot rebuild a single node”)。
请求提交后,raft_rebuild()会通过wait_for_topology_request_completion()等待重建任务完成,若失败则抛出包含错误信息的异常。这也印证了文档中的要点:rebuild 由拓扑协调器驱动,最终在目标节点上执行流式传输任务。
source-dc 参数与 --force 的语义
nodetool rebuild支持指定数据源数据中心:
- 若提供了
source-dc-name,ScyllaDB 将仅从该数据中心的节点流式传输数据(在安全的前提下); - 否则,ScyllaDB 会考虑一个没有丢失节点的替代数据中心;
- 如果不存在这样的替代数据中心,则考虑所有数据中心;
- 使用
--force选项可以强制使用指定的 source datacenter,即使这样做不安全。
“不安全”的典型场景包括:指定的源数据中心自身有节点不可达(down),此时从中重建可能无法获得完整、一致的数据。服务端在收到--force后,会将force: true作为参数传入;随后在raft_rebuild()中编码为source_dc:force提交给拓扑;执行重建的节点在 service/storage_service.cc 中解析该标记:
case node_state::rebuilding: { auto source_dc = std::get<rebuild_param>(_topology_state_machine._topology.req_param[id]).source_dc; ... utils::optional_param sdc_param; bool force; if ((force = source_dc.ends_with(":force"))) { source_dc.resize(source_dc.size() - 6); } if (!source_dc.empty()) { sdc_param.emplace(source_dc).set_user_provided().set_force(force); } ... }对应地,nodetool 工具与 REST 层的参数传递在测试中有明确的断言,参见 test/nodetool/test_rebuild.py:
test_rebuild:不带参数,期望请求POST /storage_service/rebuild(无参数);test_rebuild_source_dc:nodetool rebuild UNKNOWN_DC,期望参数source_dc=UNKNOWN_DC;test_rebuild_force_source_dc:nodetool rebuild --force UNKNOWN_DC,期望参数force=true与source_dc=UNKNOWN_DC;test_rebuild_force_no_source_dc:nodetool rebuild --force,期望参数force=true(不带 source_dc)。
RBNO 模式:Repair-Based Rebuild
文档指出,当在 Repair Based Node Operations (RBNO) 中启用 rebuild 时,数据通过repair-based-rebuild重建:即读取每个 token range 的所有源副本,并修复它们之间的任何差异(discrepancies)。否则,重建每个 token range 时数据从单个源副本流式传输。
源码中的分支逻辑位于 service/storage_service.cc:
if (is_repair_based_node_ops_enabled(streaming::stream_reason::rebuild)) { co_await _repair.local().rebuild_with_repair(std::move(ks_erms), tmptr, std::move(sdc_param), session); } else { auto streamer = make_lw_shared<dht::range_streamer>(_stream_manager, tmptr, _abort_source, ...); streamer->add_source_filter(std::make_unique<dht::range_streamer::failure_detector_source_filter>(_gossiper.get_unreachable_members())); if (source_dc != "") { streamer->add_source_filter(std::make_unique<dht::range_streamer::single_datacenter_filter>(source_dc)); } for (const auto& [keyspace_name, erm] : ks_erms) { auto ranges = co_await get_ranges_for_endpoint(*erm, my_host_id()); co_await streamer->add_ranges(keyspace_name, erm, std::move(ranges), _gossiper, false); } co_await streamer->stream_async(); }两种模式的关键区别:
| 维度 | 流式传输模式(默认) | RBNO(repair-based rebuild) |
|---|---|---|
| 数据源 | 每个 token range 从单个源副本流式传输 | 读取每个 token range 的所有源副本并修复差异 |
| 可靠性 | 依赖源副本数据的完整性 | 多副本交叉校验,更稳健、更安全(RBNO 文档的定位) |
| 实现路径 | dht::range_streamer+ 流式传输 | repair_service::rebuild_with_repair(repair/repair.cc) |
| 启用方式 | 默认 | 通过 RBNO 配置项启用 |
在流式传输模式下,源码还通过failure_detector_source_filter过滤掉 gossip 中不可达的节点,这正对应文档中“替代数据中心/全部数据中心”的选取逻辑——不可达节点所在的数据中心会被视为“不安全”而避开。
在 RBNO 模式下,rebuild_with_repair()首先确认this_shard_id() == 0(修复协调在 shard 0 上执行),若未显式指定 source_dc 则默认取本节点所在的数据中心:
const auto& topology = tmptr->get_topology(); if (!source_dc) { source_dc = utils::optional_param(topology.get_datacenter()); }随后调用通用的do_rebuild_replace_with_repair()执行逐 range 的修复式重建,并在完成后触发所有非系统表的 off-strategy compaction(trigger_offstrategy_compaction()),确保重建进来的数据在后台被正确压缩整理。
如何启用 RBNO 模式下的 rebuild
RBNO 的启用方式详见 docs/operating-scylla/procedures/cluster-management/repair-based-node-operation.rst,核心配置项包括:
enable_repair_based_node_ops=true|false:总开关,启用或禁用 RBNO;- 指定为哪些节点操作启用 RBNO 机制的配置项(rebuild、replace、decommission、removenode 等操作可分别配置)。
典型使用场景:向已有集群添加新数据中心
文档明确给出一个典型场景:向已有的 ScyllaDB 集群添加新的数据中心(DC)时,应使用 rebuild 命令。详细步骤参见 docs/operating-scylla/procedures/cluster-management/add-dc-to-existing-dc.rst。
基本用法示例:
nodetool rebuild <source-dc-name>例如,假设新数据中心的节点需要从已有的dc1拉取数据:
nodetool rebuild dc1新增 DC 后,新节点会获得由NetworkTopologyStrategy分配的新 token 范围,而这些范围的数据原本不存在于该节点上,因此需要执行 rebuild 从源 DC 的副本节点流式传输数据。这也是 rebuild 与 bootstrap 最大的不同:bootstrap 在新节点加入时就完成了范围分配与数据流式传输,而 rebuild 用于已加入的节点在获得新范围后补齐数据。
后台运行特性
文档特别提醒:ScyllaDB 的 rebuild 过程会继续在后台运行,即使 nodetool 命令被杀掉或中断。
这与源码实现一致:nodetool 只是通过 REST API(POST /storage_service/rebuild)发起请求,实际的拓扑请求与流式传输任务由 ScyllaDB 服务端(storage_service的raft_rebuild+ 拓扑协调器)接管,并作为后台任务持续执行。因此运维人员不应通过“杀掉 nodetool 进程”来中止 rebuild,而应通过nodetool netstats(查看流式传输进度)等手段观察其进展。
与 tablet keyspace 的关系(重要限制)
文档明确指出:nodetool rebuild命令只适用于 vnode keyspace。对于 tablet keyspace,应改用nodetool cluster repair。
源码在 service/storage_service.cc 中对此做了显式处理——当存在 tablet 启用的 keyspace 时,会记录一条警告日志:
“Rebuild is not supported for the following tablets-enabled keyspaces: ... Rebuild is not required for tablets-enabled keyspace after increasing replication factor. However, recovering from local data loss on this node requires running repair on all nodes in the datacenter”
也就是说:
- 对 tablet keyspace提高副本因子(replication factor)后,无需执行 rebuild(tablet 负载均衡会自动处理新副本的数据迁移);
- 若 tablet keyspace 发生本地数据丢失,需要在该数据中心的所有节点上运行 repair 来恢复,而不是 rebuild。
关于 tablet 与 vnode 的数据分布差异,可进一步阅读 docs/architecture/tablets.rst(文档中引用为 Data Distribution with Tablets)。
使用建议与注意事项
综合文档与源码,使用nodetool rebuild时的实践建议如下:
- 确认 keyspace 类型:执行前先确认目标节点上的 keyspace 是否为 vnode 模式;若包含 tablet keyspace,则按上文说明改用
nodetool cluster repair; - 明确数据源 DC:多数据中心环境下,尽量显式指定
source-dc-name,避免 ScyllaDB 自动选择替代 DC 或全集群数据源带来的额外跨 DC 流量; - 谨慎使用
--force:只有在明确了解源 DC 存在不可达节点风险、且接受可能的数据不一致时,才使用--force; - 不要中断命令进程:rebuild 在后台持续运行,杀掉 nodetool 进程不会中止重建;
- 结合 RBNO:对数据一致性要求高的场景,可启用 RBNO 使 rebuild 走 repair-based 路径,从所有源副本交叉校验数据;
- 观察进度:通过
nodetool netstats观察流式传输进度,确认重建完成。
总结
nodetool rebuild是 ScyllaDB 在“节点数据补齐”场景下的核心运维命令:它先确定本地节点的 token 范围与持有相同范围的远端节点,再通过流式传输(默认单副本源)或 RBNO 修复式重建(多副本交叉校验)将数据补齐。source-dc控制数据源数据中心,--force则允许在不安全的情况下强制使用指定 DC。理解其背后的 topology 请求机制(service/storage_service.cc)、流式传输与 RBNO 双路径(service/storage_service.cc、repair/repair.cc)以及 vnode/tablet 的适用性差异,是正确、安全使用该命令的关键。
【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考