背景#

清理重复行、换引擎、重算报表,每一套操作都要建临时表、删临时表,有的还要删分区。删错一次的代价,取决于删的方式和两类服务端设置:

设置 上游 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 新服务,删之前的确认更要紧。

参考资料#

相关文章#