ClickHouse 恢复后数据校验

本文说明如何使用 clickhouse_ansible 新增的 validate_restore_consistency.yml,对源集群与恢复目标集群做跨集群数据验收。

这不是 restore_cluster.yml 的内置步骤,而是独立 Playbook。原因很明确:

  1. 它需要同时读取两套 inventory。
  2. 它本质上是“两套集群之间的对比任务”。
  3. 对持续写入中的生产源集群,不存在稳定的“恢复后强一致验收”窗口,只有停写/静态演练场景才适合做这种校验。

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_group
  • validate_target_group

4. 关键变量

文件:playbooks/vars/validate_restore_config.yml

变量默认值作用
validate_source_groupclickhouse_backup源集群分组
validate_target_groupclickhouse_restore目标集群分组
validate_source_query_host127.0.0.1源端查询地址
validate_target_query_host127.0.0.1目标端查询地址
validate_clickhouse_userdefault查询用户名
validate_clickhouse_password{{ clickhouse_default_password }}查询密码
validate_fail_on_missing_pairstrue源/目标缺少对应分片副本时是否失败
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() 做强一致验收。原因是:

  1. RESTORE 只是把备份中的 part 恢复回来。
  2. TTL DELETE 依赖后台 merge 异步清理,不会在恢复时瞬间全部完成。
  3. 即使手工执行 MATERIALIZE TTL,源和目标的执行时刻也不同,边界附近的数据仍可能继续漂移。

因此:

  • TTL 表优先用固定时间窗口对比
  • 非 TTL 表再考虑全表总量对比

7. 返回结果

校验通过时,Playbook 会输出:

  • source_group
  • target_group
  • checks
  • pairs

校验失败时,会直接列出 mismatch,例如:

  • 哪个 check
  • 哪个 shard/replica
  • 源端值
  • 目标值

8. 推荐顺序

  1. ClickHouse 备份
  2. ClickHouse 恢复
  3. 恢复完成后执行 validate_restore_consistency.yml
  4. 若需要清环境,再执行卸载剧本