ClickHouse 恢复后数据校验
本文说明如何使用 clickhouse_ansible 新增的 validate_restore_consistency.yml,对源集群与恢复目标集群做跨集群数据验收。
这不是 restore_cluster.yml 的内置步骤,而是独立 Playbook。原因很明确:
- 它需要同时读取两套 inventory。
- 它本质上是“两套集群之间的对比任务”。
- 对持续写入中的生产源集群,不存在稳定的“恢复后强一致验收”窗口,只有停写/静态演练场景才适合做这种校验。
1. 适用场景
- 演练环境
- 已停写的静态源集群
- 已完成
restore_cluster.yml的恢复目标集群
不建议直接用于:
- 持续写入中的生产源集群
- 业务仍在变更的恢复目标集群
2. 为什么单独做成新 Playbook
restore_cluster.yml 负责:
- 读取 manifest
- 找到备份路径
- 执行 ClickHouse
RESTORE - 校验恢复目标侧复制健康
它不适合直接负责“源集群与目标集群对比”,因为:
- 源集群可能不是当前 inventory 的一部分
- 源集群可能仍在写入
- TTL 表、异步 merge 表、物化视图表的验收口径也不一样
因此,恢复动作和恢复验收应该分开。
3. 调用方式
validate_restore_consistency.yml 需要同时传入两套 inventory:
cd /usr/local/dbbot/clickhouse_ansible/playbooks
ansible-playbook \
-i ../inventory/hosts.backup.ini \
-i ../inventory/hosts.restore.ini \
validate_restore_consistency.yml
默认约定:
- 源集群分组:
clickhouse_backup - 目标集群分组:
clickhouse_restore
如果你使用自定义分组名,可在 playbooks/vars/validate_restore_config.yml 中覆盖:
validate_source_groupvalidate_target_group
4. 关键变量
文件:playbooks/vars/validate_restore_config.yml
| 变量 | 默认值 | 作用 |
|---|---|---|
validate_source_group | clickhouse_backup | 源集群分组 |
validate_target_group | clickhouse_restore | 目标集群分组 |
validate_source_query_host | 127.0.0.1 | 源端查询地址 |
validate_target_query_host | 127.0.0.1 | 目标端查询地址 |
validate_clickhouse_user | default | 查询用户名 |
validate_clickhouse_password | {{ clickhouse_default_password }} | 查询密码 |
validate_fail_on_missing_pairs | true | 源/目标缺少对应分片副本时是否失败 |
validate_checks | [] | 校验项列表 |
5. 校验项写法
5.1 常规本地表总量校验
validate_checks:
- name: "order_items_full_count"
database: "lab_ck_biz"
local_table: "fact_order_item_local"
当 query 为空时,Playbook 会自动生成:
SELECT toString(count()) FROM lab_ck_biz.fact_order_item_local
5.2 TTL 表固定时间窗口校验
这是当前最推荐的写法。
validate_checks:
- name: "mysql_slowlog_recent_window"
database: "lab_ck_biz"
local_table: "mysql_slowlog_raw_local"
where: "log_time >= toDateTime('2026-02-07 00:00:00')"
Playbook 会自动生成:
SELECT toString(count())
FROM lab_ck_biz.mysql_slowlog_raw_local
WHERE log_time >= toDateTime('2026-02-07 00:00:00')
5.3 自定义单值 SQL
如果要做更复杂的验收,可以直接写完整 SQL,但结果必须是单值:
validate_checks:
- name: "slowlog_recent_sum_rows_examined"
query: "SELECT toString(sum(rows_examined)) FROM lab_ck_biz.mysql_slowlog_raw_local WHERE log_time >= toDateTime('2026-02-07 00:00:00')"
6. 为什么 TTL 表不能直接比全表总数
像 mysql_slowlog_raw_local 这种表,默认是:
- 造数跨度
60天 - 表 TTL
30 DAY DELETE
这种表即使恢复成功,也不适合直接拿源端和目标端的全表 count() 做强一致验收。原因是:
RESTORE只是把备份中的 part 恢复回来。TTL DELETE依赖后台 merge 异步清理,不会在恢复时瞬间全部完成。- 即使手工执行
MATERIALIZE TTL,源和目标的执行时刻也不同,边界附近的数据仍可能继续漂移。
因此:
- TTL 表优先用固定时间窗口对比
- 非 TTL 表再考虑全表总量对比
7. 返回结果
校验通过时,Playbook 会输出:
source_grouptarget_groupcheckspairs
校验失败时,会直接列出 mismatch,例如:
- 哪个
check - 哪个
shard/replica - 源端值
- 目标值
8. 推荐顺序
- ClickHouse 备份
- ClickHouse 恢复
- 恢复完成后执行
validate_restore_consistency.yml - 若需要清环境,再执行卸载剧本