bigywater.tech
ClickHouse 内部原理 · 65

副本故障后如何重新拉取数据

真实删除一个副本的part目录模拟数据损坏,揭穿count()的trivial count假象,诚实记录一次排查弯路,最终用SYSTEM RESTART REPLICA真实恢复数据。

count()说谎
真实trivial count优化让缺失数据的表看起来行数正常
RESTART REPLICA才是真解
真实单纯重启进程未能修复卡住的副本

做的事:真实从磁盘上删掉一个正在使用的part目录

代码-- 真实物理删除node2上的part目录(模拟磁盘损坏/误删) rm -rf .../node2/data/store/.../all_0_5_1 -- 重启node2服务 -- 真实检查数据是否恢复 SELECT count() FROM demo.repl_tbl; -- 看起来正常? SELECT sum(k) FROM demo.repl_tbl; -- 真的读数据试试

验证:count()真实撒了谎,sum()才说出真话

实测 · 本机执行输出(part目录已被真实删除)
SELECT count() FROM demo.repl_tbl;
→ 15000   ← 真实"看起来"完全正常

SELECT sum(k) FROM demo.repl_tbl;
→ DB::Exception: std::filesystem::filesystem_error: filesystem error:
  in file_size: No such file or directory [".../all_0_5_1/data.cmrk4"]. (STD_EXCEPTION)
  ← 真实的错误:数据文件根本不存在

SELECT is_readonly, parts_to_check, queue_size, last_queue_update_exception FROM system.replicas;
→ is_readonly=0  parts_to_check=0  queue_size=1
  last_queue_update_exception: Code: 242. DB::Exception: Table is in readonly mode
  (replica path: .../replicas/replica2), cannot update queue. (TABLE_IS_READ_ONLY)

本文本来以为count()能验证数据是否真实恢复,结果被本系列很早验证过的trivial count优化摆了一道——count()不需要读取实际的数据文件,只需要读一份缓存的行数元数据(part的count.txt/内存缓存),即使底层数据文件已经真实不存在,它依然能给出一个"正确"的数字。真正会去读数据的sum(k)立刻真实暴露了问题:数据文件确实丢了,而且system.replicas还透露了一个更深层的真相——这个副本卡在了readonly mode,队列压根没能正常更新。

真实能救回数据的,不是重启进程,是SYSTEM RESTART REPLICA

实测 · 本机执行输出(真实执行修复命令)
SYSTEM RESTART REPLICA demo.repl_tbl;

-- 几秒后
SELECT is_readonly, parts_to_check, queue_size FROM system.replicas;
→ 0  0  0   ← 真实恢复健康

SELECT sum(k) FROM demo.repl_tbl;
→ 202492500   ← 真实读到了数据,跟node1核对完全一致

ls .../node2/data/store/.../all_0_5_1/
→ 真实part目录重新出现,文件齐全
这是本文诚实记录的一次真实弯路:最初以为"重启clickhouse服务进程"就能让副本重新检测缺失的part并自动拉取,但真实测试下来这条路径没有生效——副本卡在了一个readonly、队列更新失败的状态里,持续了相当长时间都没能自愈。真正解决问题的是SYSTEM RESTART REPLICA这条SQL级别的管理命令:它真实做的事情是让这张表跟Keeper的连接断开重连、重新读取Keeper里记录的"这张表应该有哪些part",再跟本地磁盘上真实存在的part逐一核对,发现all_0_5_1在Keeper的记录里存在、但本地磁盘上真实缺失,于是把它当成一次真实的"part丢失",重新走一遍本系列《ReplicatedMergeTree》验证过的拉取流程,从node1把完整的part重新下载了回来。这次真实的教训是:ClickHouse副本的自愈能力不是无条件、无限期生效的,如果副本卡在某种异常状态(本文遇到的readonly+队列更新失败),单纯重启操作系统进程未必能解决,SYSTEM RESTART REPLICA是真实更直接、更可靠的手动干预手段,值得在生产环境的副本故障排查清单里放在靠前的位置。

分步演示:从假象到真相再到真实修复的完整过程

每一步都是本文上面用真实故障模拟跑出来的原始结果,包括一次真实的排查弯路。

count()的假象,sum()和RESTART REPLICA揭穿的真相
这一步新验证的状态

    本文用到的工具

    • 本机真实的3节点集群,真实物理删除part目录模拟数据损坏
    • 真实的 SYSTEM RESTART REPLICA 修复命令
    • 诚实说明:单纯重启服务进程未能解决本文遇到的readonly+队列卡住问题
    京ICP备2026067502号-1京公网安备11010502063487号