ScyllaDB 集群安全关机(Safe Shutdown)完整指南:drain、验证与重启全流程
【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb
本文基于 safe-shutdown.rst 官方文档,完整讲解在物理搬迁硬件或计划性停机场景下,如何以不损坏数据、不触发额外修复的方式安全关闭 ScyllaDB 集群,并配套源码级原理剖析与重启(safe-start)衔接流程。读完本文,你将掌握
nodetool drain+nodetool status+ 停止服务的标准三步法,并能根据节点状态码判断停机是否真正成功。
为什么需要"安全关机"而不是直接关电源
ScyllaDB 是一个基于 Seastar 框架的 NoSQL 数据存储,兼容 Apache Cassandra 与 Amazon DynamoDB。它在内存(memtable)中缓存大量近期写入数据,并以周期性刷盘(flush)的方式持久化到磁盘上的 SSTable 文件。如果直接断电或强制 kill 进程,未刷盘的 memtable 数据虽可通过 commitlog 在重启时重放恢复,但代价是:重启时需要进行崩溃恢复(replay commitlog),在集群多个节点同时重启的场景下,还可能触发大范围的流式修复(repair)、hinted handoff 投递积压,显著拉长集群恢复时间,甚至放大停机窗口内的风险。
因此官方文档给出的做法是:在需要物理移动硬件、或别无选择必须停机时,先执行 drain 让节点优雅地把内存数据全部落盘,再干净地停止服务,从而把重启成本降到最低。
操作前检查(Before you begin)
官方文档明确要求在动手之前确认一件事:
确认没有任何应用程序正在把该集群作为后端存储使用。
这一步非常关键:nodetool drain执行后节点会停止监听来自客户端和其他节点的连接,此时节点上的读写请求会立即失败。如果在业务高峰期或有在线应用持续读写时执行 drain,会造成明显的请求中断。因此应先在应用层完成流量摘除(例如停止写入任务、切换只读、或让客户端连接到其他节点),再对目标节点执行关机流程。
安全关机三步法(在每个节点上并行执行)
官方流程要求在集群的每个节点上、以并行方式依次执行以下三个步骤。并行执行的含义是:不要串行地逐台关机,而是各节点几乎同时完成 drain → 验证 → 停止,以缩短集群整体不可用窗口。
第 1 步:执行nodetool drain
nodetool drain根据 drain 命令文档 的定义,该命令会:
- 将节点上所有 memtable 刷写(flush)为磁盘上的 SSTable;
- 停止监听来自客户端和其他节点的连接;
- 执行完成后需要重启 ScyllaDB 才能恢复服务。
该命令通常用于节点升级前、或任何维护动作之前。从 tools/scylla-nodetool.cc 的源码可以看到它的帮助文本与官方文档一致:
Drain the node (stop accepting writes and flush all tables) Flushes all memtables from a node to the SSTables that are on the disk. Scylla stops listening for connections from the client and other nodes. You need to restart Scylla after running this command. ...注意:如果你只想把 memtable 刷盘、而不需要停止节点对外服务,应当使用 nodetool flush 而不是 drain。flush 的语法为:
nodetool flush # 刷写所有 keyspace nodetool flush keyspace1 # 刷写指定 keyspace nodetool flush keyspace1 standard1 # 刷写指定 keyspace 的指定表<keyspace>与<table> ...均为可选参数:省略 keyspace 时刷写全部 keyspace;指定 keyspace 后可进一步限定一张或多张表。
第 2 步:用nodetool status验证 drain 完成
nodetool statusdrain 命令本身在正常完成后会正常返回,但官方流程仍要求用nodetool status二次确认。判断标准非常明确:
如果节点状态被列为
DN,则说明 drain 命令已成功执行。
理解DN的含义需要拆解 nodetool status 输出的两列标记:
- Status 列:
U= Up(节点在线)、D= Down(节点离线)、X= excluded(已被removenode、excludenode或节点替换标记为永久丢失的宕机节点); - State 列:
N= Normal、L= Leaving、J= Joining、M= Moving。
所以DN表示节点处于Down + Normal:节点已停止对外服务但状态正常(并未处于离开/加入/移动等过渡状态),这正是 drain 成功后节点应有的表现。而正常运行时节点应显示为UN(Up + Normal),例如:
Datacenter: datacenter1 ======================= Status=Up/Down/eXcluded |/ State=Normal/Leaving/Joining/Moving -- Address Load Tokens Owns (effective) Host ID Rack UN 127.0.0.1 394.97 MB 256 33.4% 292a6c7f-2063-484c-b54d-9015216f1750 rack1 UN 127.0.0.2 151.07 MB 256 34.3% 102b6ecd-2081-4073-8172-bf818c35e27b rack1 XN 127.0.0.4 149.07 MB 256 32.3% dd961642-c7c6-4962-9f5a-ea774dbaed77 rack1第 3 步:drain 成功后停止节点
确认节点状态为DN后,即可停止该节点的 ScyllaDB 服务。根据 scylla-commands-stop-index.rst,停止命令因部署方式而异:
支持的操作系统(systemd 安装):
sudo systemctl stop scylla-serverDocker 部署(注意:只停止容器内的 scylla 进程,而不停止容器本身):
docker exec -it some-scylla supervisorctl stop scylla上面的
some-scylla是容器名,需替换为你实际的容器名称。
三个步骤在所有节点上并行完成后,集群即处于完全停机状态,此时可以进行物理搬迁等操作。
drain 的底层原理(源码佐证)
从源码看,nodetool drain最终通过 HTTP REST API 触发服务端的 drain 逻辑。在 api/storage_service.cc 中:
static future<json::json_return_type> rest_drain(sharded<service::storage_service>& ss, std::unique_ptr<http::request> req) { apilog.info("drain"); return ss.local().drain().then([] { return make_ready_future<json::json_return_type>(json_void()); }); }即调用storage_service::drain()完成核心工作。对应的 API 定义位于 api/api-doc/storage_service.json,同时暴露了两个端点:
/storage_service/drain(nickname:drain)——触发 drain;- 进度查询端点(nickname:
get_drain_progress)——用于获取 drain 进度。
进度查询在 api/storage_service.cc 中实现,通过replica::database::get_drain_progress()汇总各 shard 的进度,并输出形如Drained {remaining_cfs}/{total_cfs} ColumnFamilies的进度信息:
future<json::json_return_type> rest_get_drain_progress(sharded<service::storage_service>& ss, std::unique_ptr<http::request> req) { return ss.map_reduce(adder<replica::database::drain_progress>(), [] (auto& ss) { return ss.get_database().get_drain_progress(); }).then([] (auto&& progress) { auto progress_str = format("Drained {}/{} ColumnFamilies", progress.remaining_cfs, progress.total_cfs); return make_ready_future<json::json_return_type>(std::move(progress_str)); }); }从这段实现可以推断,drain 是一个逐 ColumnFamily(表)串行刷盘的过程:remaining_cfs表示尚未完成刷写的表数,total_cfs表示节点上表的总数,当两者相等时 drain 完成。这也解释了为什么文档要求在执行 drain 后必须确认其完成——如果表数量很大,drain 可能持续一段时间。
另外在 api/storage_service.cc 中可以看到drain与get_drain_progress两个 REST 处理器是成对注册的,它们共用同一个 gating(gated),确保在节点 teardown 时先排空在途请求再关闭,进一步印证了"先优雅排空、再停止"的设计哲学。
重新启动集群(Safe Start 衔接)
安全停机之后的恢复流程记录在配套文档 Start Clusters Cleanly 中,它明确要求:启动前必须确认集群是按照 safe-shutdown 流程关闭的,否则应走正常的崩溃恢复路径。
启动步骤同样是并行执行:
并行启动各节点。根据 scylla-commands-start-index.rst:
支持的操作系统:
sudo systemctl start scylla-serverDocker 部署(容器已在运行的前提下):
docker exec -it some-scylla supervisorctl start scylla
验证所有节点恢复正常:运行
nodetool status,如果每个节点的状态都是UN(Up + Normal),则说明启动成功。
因为停机前已经通过 drain 将所有 memtable 落盘,节点重启时无需重放大量 commitlog,也不会有大量积压的流式修复,集群可以快速、平稳地回到UN状态。
常见误区与注意事项
- 不要把
nodetool flush当drain用:flush 只刷盘不切断服务,适合日常维护;drain 会停止监听客户端和节点间连接,执行后必须重启才能恢复服务,二者用途不可混淆。 - 不要在业务流量未摘除时 drain:drain 后节点立即停止接受读写,应用会报连接失败,务必先确认没有应用把集群当作后端存储。
DN才是 drain 成功的标志:若 status 中节点仍显示UN,说明 drain 未完成或未生效,不应继续执行 stop,否则就退化为"非安全关机"。- Docker 环境下停止的是容器内进程:
supervisorctl stop scylla只停 scylla 服务,容器本身保持运行,这也是为了便于后续用supervisorctl start scylla快速恢复。 - 集群恢复要验证到
UN:不要以为节点进程起来了就算成功,必须以nodetool status中每个节点均为UN为准,确认节点间 gossip 正常、数据副本状态一致后再恢复应用流量。
小结
ScyllaDB 集群安全关机不是简单的"停服务",而是一套"先排空内存、再验证、后停止"的优雅停机流程:在每台节点上并行执行nodetool drain刷盘并切断连接 → 用nodetool status确认节点进入DN状态 → 按部署方式(systemd 或 Docker)停止服务 → 后续按 safe-start 流程并行启动并验证UN。结合 api/storage_service.cc 等源码可以看到,drain 本质上是逐 ColumnFamily 刷盘的受控过程,并配有专门的进度查询端点。掌握这一流程,可以在物理搬迁、计划维护等场景下把集群停机与重启的数据风险降到最低。
【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考