ScyllaDB 卡在 JOINING (UJ) 状态节点的安全移除指南:drain、清理数据与重新加入集群
【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb
导读:当向 ScyllaDB 集群添加新节点时,节点可能长期停留在 Up-Joining (UJ) 状态、始终无法进入 Up-Normal (UN) 状态,此时唯一的解决办法是移除该节点并重试。本文基于官方运维手册 safely-removing-joining-node.rst,完整讲解"drain → 停止 → 清理数据 → 启动"四步操作,并结合仓库源码剖析nodetool drain的底层实现,帮助你在不破坏集群数据的前提下安全处置卡住的加入中节点。
问题背景:什么是 JOINING (UJ) 状态
在 ScyllaDB 中,向集群添加新节点是一个涉及数据流(streaming)的过程:新节点加入后,其他节点会把属于新节点 token 范围的数据流式传输给它。在此期间,通过nodetool status观察,新节点会显示为UJ(Up, Joining)状态——即进程已启动、正在加入中但尚未完成数据同步。当数据同步完成、节点进入正常服务状态后,状态才会变为UN(Up, Normal)。这一过程的时间长短取决于数据量和网络带宽(参见 add-node-to-cluster.rst)。
然而在某些情况下,新节点可能卡死在 JOINING 状态,长时间停留在 UJ 而永远无法进入 UN。官方文档给出的结论是:
唯一的解决方案是移除该节点。
同时文档给出了一个关键的安全边界:
只要节点从未进入 UN 状态(即从未真正加入集群),你就可以停止该节点、清理它的数据,然后重新尝试加入。
也就是说,本方案适用于尚未成功加入集群的节点。如果一个节点已经进入过 UN 状态(已正常服务),那就不能再简单采用"停止+清理+重试"的方式,否则会破坏集群的副本数据布局,此时应改用其他集群管理流程。
操作前提与安全边界确认
在动手之前,请务必确认以下两点:
- 确认节点确实处于 UJ 状态:运行
nodetool status,观察目标节点的状态标识。只有确认其显示为UJ(Up Joining)且长时间不变化,才适用本文流程。 - 确认该节点从未进入 UN 状态:这是整个操作安全性的核心前提。未加入集群的节点上没有集群数据的所有权,清理其本地数据不会影响集群其他节点上的数据副本。
官方手册 cluster-platform-migration.rst 也给出了同样的判断:如果新节点长时间停留在UJ状态,应当按本文流程处理。
四步操作流程详解
以下命令全部在卡住的节点本机上执行。
第一步:执行 nodetool drain
nodetool drainnodetool drain会将该节点上所有 memtable 刷新(flush)到磁盘上的 SSTable,同时停止监听来自客户端和其他节点的连接。官方对 drain 命令的完整定义参见 drain.rst:它会停止 ScyllaDB 接收客户端与其他节点的连接,且执行后必须重启 ScyllaDB。该命令通常在执行节点升级或任何维护操作前使用。
注意:如果你只是想简单地把 memtable 刷到磁盘(不停止连接),应使用
nodetool flush而不是nodetool drain。
第二步:停止节点
受支持的 Linux 发行版(通过 systemd 安装):
sudo systemctl stop scylla-serverDocker 部署方式:
docker exec -it some-scylla supervisorctl stop scylla(注意:上述命令只停止容器内的 ScyllaDB 进程,不会停止some-scylla容器本身。执行后容器仍在运行,便于下一步清理数据文件。)
第三步:清理节点数据
这是最关键的一步——清空该节点在加入过程中产生的所有本地数据,使其恢复到"干净"状态,以便重新加入:
sudo rm -rf /var/lib/scylla/data sudo find /var/lib/scylla/commitlog -type f -delete sudo find /var/lib/scylla/hints -type f -delete sudo find /var/lib/scylla/view_hints -type f -delete清理对象共四类:
| 目录 | 清理命令 | 说明 |
|---|---|---|
/var/lib/scylla/data | sudo rm -rf | 数据目录,删除全部 SSTable 与相关数据文件 |
/var/lib/scylla/commitlog | sudo find ... -type f -delete | 提交日志,删除文件但保留目录 |
/var/lib/scylla/hints | sudo find ... -type f -delete | 提示(hinted handoff)文件 |
/var/lib/scylla/view_hints | sudo find ... -type f -delete | 物化视图相关的提示文件 |
路径可能不同:以上路径是默认值。ScyllaDB 的数据目录、提交日志目录与提示目录均可在配置文件 conf/scylla.yaml 中调整(对应data_file_directories(第 32 行)、commitlog_directory(第 37 行)、hints_directory(第 284 行)、view_hints_directory(第 287 行))。如果你的节点修改过这些配置,请将命令中的路径替换为实际配置值。
第四步:启动节点
受支持的 Linux 发行版:
sudo systemctl start scylla-serverDocker 部署方式:
docker exec -it some-scylla supervisorctl start scylla(前提:some-scylla容器已在运行中。)
启动完成后,ScyllaDB 会重新执行加入流程,节点将以 UJ 状态再次尝试加入集群,这次通常会正常完成数据流并进入 UN 状态。
源码视角:nodetool drain 到底做了什么
为了确保你在执行 drain 后可以安全地停止并清理节点,有必要理解其底层实现。在 ScyllaDB 源码中,drain 的 REST API 入口位于 api/storage_service.cc,它会调用storage_service::drain();其核心实现在 service/storage_service.cc:
- 节点进入
DRAINING模式后执行do_drain(),完成后将_drain_finished置位并切换到DRAINED模式;如果节点已经处于DRAINED状态,再次执行会输出警告Cannot drain node (did it already happen?)并直接返回。 do_drain()(service/storage_service.cc)的执行顺序可以概括为:- 停止传输层(
stop_transport()):关闭与客户端及其他节点的连接——这正是 drain 命令"停止监听"语义的来源; - 取消非本地写响应处理器(
cancel_nonlocal_write_response_handlers()):释放可能被挂起写请求持有的 token_metadata 版本,避免后续等待 group0 停止时发生死锁; - 排空视图构建器(
view_builder/view_building_worker的drain):在停止 group0 之前完成视图构建的排空; - 继续排空 group0、提交日志(commitlog)、memtable 等本地存储组件,将内存数据完整落盘。
- 停止传输层(
从源码可以看出,drain 是一个"先切断外部联系、再有序落盘"的受控关闭过程。正因为 drain 完成后节点已不再对外服务且数据已刷盘,接下来停止服务、清理数据目录才不会有在途请求或未落盘数据丢失的风险。
常见问题与注意事项
可以跳过 drain 直接停止吗?不推荐。文档明确将 drain 列为第一步。跳过 drain 意味着停止前可能还有在途连接与未刷盘的 memtable,直接停止可能造成数据不一致或拖慢后续清理。
执行 drain 后节点会自动重启吗?不会。drain 只会让节点进入 DRAINED 状态并停止监听,后续的停止、清理、启动都需要你按步骤手动执行。
清理数据会丢失集群数据吗?不会。前提是节点从未进入 UN 状态——此时它尚未成为集群数据副本的持有者,清理的只是它本地在加入过程中收到的临时数据。集群其他节点上的数据不受影响。
重新加入后仍然卡在 UJ?如果清理数据后重试,节点依然长时间停留在 UJ,则说明问题不在本地数据,而更可能出在网络、磁盘空间或集群拓扑层面,需要结合日志进一步排查(可参考 node-joined-without-any-data.rst 中关于 UJ 状态的分析思路)。
命令需要 root 权限:无论是 systemd 还是 Docker 方式,停止服务与清理
/var/lib/scylla下的文件都需要sudo(或在容器内使用具有足够权限的用户)。
总结
处理卡在 JOINING (UJ) 状态、始终无法进入 UN 状态的 ScyllaDB 节点,标准做法就是四步:nodetool drain受控停止服务 → 停止节点进程 → 清理data/commitlog/hints/view_hints四个目录 → 重新启动节点使其再次尝试加入。这套流程安全的前提是节点从未真正加入集群;一旦确认这一前提,就可以放心地清理本地数据并重试。掌握这一流程,你就能在扩容失败时快速恢复集群的可用状态,而无需动用更复杂的集群管理操作。
【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考