ClickHouse 误删表和分区之后的恢复
目录
背景#
清理重复行、换引擎、重算报表,每一套操作都要建临时表、删临时表,有的还要删分区。删错一次的代价,取决于删的方式和两类服务端设置:
| 设置 | 上游 25.3 默认 | Aiven 上的现有集群 |
|---|---|---|
database_atomic_delay_before_drop_table_sec |
480 秒 | 0 |
max_table_size_to_drop / max_partition_size_to_drop |
50000000000 字节(50 GB) | 0,即不限 |
现有集群这一列来自 Aiven 服务端设置的只读导出。前一项是 DROP TABLE 之后的宽限期,宽限期内能 UNDROP;后两项是删大表、大分区的保险。现有集群上两样都没有。
上游默认下每种删法能救回什么,在 clickhouse-lab 的实验 07、13 里各做过一次(25.3.14.14,Atomic 库)。实验 13 里删表的几段,有的只在 ch1 上删,有的用的是单副本的表,是为了直接看 Keeper 里那个副本节点的去留。
上游默认下,各种删法能救回什么#
| 删法 | 救法 | 实验 13 的结果 |
|---|---|---|
DETACH PARTITION |
ATTACH PARTITION |
可逆,行数原样回来。数据只是摘下来,在 system.detached_parts 里看得到,这不算删除 |
DROP PARTITION |
只能靠事先的 FREEZE 备份,没冻过就没有 |
不留 detached,没有 UNDROP。把 shadow/ 里那个分区的 part 目录拷进表的 detached/,再 ATTACH PARTITION,三个副本都回来了 |
DROP TABLE(不加 SYNC) |
UNDROP TABLE |
宽限期内原样救回,数据完整,期间表在 system.dropped_tables 里 |
DROP TABLE 之后先 SYSTEM DROP REPLICA 再 UNDROP |
UNDROP 加 SYSTEM RESTORE REPLICA |
表和数据都回来,但先是只读的,要按本地 part 重建 Keeper 里的元数据之后才能写 |
DROP TABLE … SYNC |
救不回来 | UNDROP 报 Code: 60 … UNKNOWN_TABLE,SYNC 是立刻删,同时放弃了后悔药 |
FREEZE 冻的是硬链接,当时不额外占盘,原 part 被合并掉之后这一份才真正占住空间。它只冻执行它的那个副本:ch1 上冻了,ch2 上没有 shadow/ 目录。还原的时候只在 ch1 上 ATTACH,另外两个副本自己从 ch1 拉回去,所以一份冻结备份够整个副本组还原。实验里是 3 行的玩具表,只说明步骤走得通,真实量级上拷目录和副本间拉取要多久没测过。
Keeper 残留副本和 UNDROP 共用一个宽限期#
DROP TABLE 不加 SYNC 时,表进入延迟删除,要等宽限期满才真删,这期间 Keeper 里还挂着这张表的副本节点。实验 13 用 SLOW=1 等满了一次,每 10 秒同时看 Keeper 里的副本和 system.dropped_tables 的条目:两者都在第 484 秒左右、同一个 10 秒轮询里一起消失,之后 UNDROP 报 UNKNOWN_TABLE。所以看到残留的副本还在,就说明还来得及 UNDROP。
这个宽限期内要做两件事时有先后。想救表就先 UNDROP;先用 SYSTEM DROP REPLICA 清了 Keeper 残留再 UNDROP,表能回来、数据也在,但回来是只读的,写入报 Code: 242 … table metadata was not found in zookeeper,要再跑一次 SYSTEM RESTORE REPLICA(实验 13)。
清残留那条命令本身有个坑:副本名不做宏替换。写成 SYSTEM DROP REPLICA '{replica}' FROM ZKPATH '…' 不报错也不生效,是个静默空跑,副本名要写字面量(实验 07)。下面是 lab 上的写法,路径是 lab 的:
SELECT getMacro('replica'); -- 查出副本名,下一条要用字面量
SYSTEM DROP REPLICA 'ch1' FROM ZKPATH '/ch/tables/01/<表名>';
残留副本还会让同名、同 Keeper 路径的表建不起来,清理 runbook 里工作表一律带 SYNC 删,原因写在 REPLACE PARTITION runbook 的 R0。现有集群的宽限期是 0,没有残留副本,也没有 UNDROP 的窗口,这一节的内容只在上游默认下成立。
删大表的保险#
上游默认删超过 50 GB 的表或分区会被服务端拒绝,报 Code: 359。这是一道保护,报错不是故障。实验 13 把阈值压到 1 字节模拟「表比阈值大」,DROP PARTITION 和 DROP TABLE 都被拒,表还在。两项既是服务端设置,也是查询级设置,确认删的就是想删的东西之后,可以只对这一条语句放开,不用改服务端配置:
ALTER TABLE t DROP PARTITION ID '20260310' SETTINGS max_partition_size_to_drop = 0;
DROP TABLE t SETTINGS max_table_size_to_drop = 0;
现有集群上还剩哪些救法#
UNDROP 只在宽限期内有效(UNDROP TABLE),现有集群的宽限期是 0,DROP TABLE 不加 SYNC 也没有能救回的窗口;大小保险是 0,删大表、大分区不会再被 Code: 359 拦下。现有集群用的又是 Replicated 库,库里的 DDL 像 ON CLUSTER 一样在所有副本上执行(Replicated 库引擎),一条 DROP TABLE 就把三个副本上的表都删了。
对照上面那张表,现有集群上还剩下的救法只有两条:DETACH 本来就不算删除;其余几种删法,都只能靠备份。备份由 Aiven 做,查询日志里看到的是 ALTER TABLE … FREEZE,一次几分钟。按 Aiven 的文档,还原是从备份 fork 出一个新服务,再把应用的连接改到新服务上(Restore an Aiven for ClickHouse backup),不是在原服务上原地恢复;上一次备份之后写进去的数据,要靠上游重发补回来(推断)。真实量级上整个过程要多久,没演练过。
所以在现有集群上,删之前的检查比删之后的补救重要。清理重复行和换引擎的 runbook 最后都要删临时表、快照表或旧表,那一步执行前再确认一次表名和大小;换引擎之后的旧表、清理用的快照表,按 runbook 留到对账和下游确认之后再删(REPLACE PARTITION runbook、在线换引擎)。
小结#
- 上游默认下:
DETACH可逆;DROP PARTITION只能靠事先的FREEZE备份;DROP TABLE不加SYNC有 480 秒能UNDROP;加了SYNC就救不回来。 - 宽限期内,Keeper 里的残留副本和
system.dropped_tables的条目同时存在、同时消失;先清残留再UNDROP的表回来是只读的,要RESTORE REPLICA;清残留时副本名要写字面量。 - 删超过 50 GB 的表或分区默认会被拒,可以只对一条语句放开。
- Aiven 上的现有集群里宽限期和大小保险都是 0,
Replicated库里一条DROP删掉三个副本,误删之后只能从备份 fork 新服务,删之前的确认更要紧。
参考资料#
- UNDROP TABLE:只在
database_atomic_delay_before_drop_table_sec的宽限期内有效 - ALTER TABLE … FREEZE PARTITION
- SYSTEM DROP REPLICA / RESTORE REPLICA
- Replicated 库引擎:DDL 像
ON CLUSTER一样执行 - Restore an Aiven for ClickHouse backup:从备份 fork 出新服务
- clickhouse-lab 实验 13、实验 07:输出在
results/下的同名文件