真实删除一个副本的part目录模拟数据损坏,揭穿count()的trivial count假象,诚实记录一次排查弯路,最终用SYSTEM RESTART REPLICA真实恢复数据。
代码-- 真实物理删除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; -- 真的读数据试试
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 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目录重新出现,文件齐全
每一步都是本文上面用真实故障模拟跑出来的原始结果,包括一次真实的排查弯路。
SYSTEM RESTART REPLICA 修复命令