bigywater.tech
ClickHouse 内部原理 · 28

Mutation:ALTER UPDATE/DELETE 为什么很重

真实对300万行表执行只影响1/3数据的ALTER TABLE UPDATE,用part文件名和字节数变化证明mutation真实是整part重写而非原地patch。

只改1/3
真实的UPDATE条件只命中全表1/3的行
3个part全重写
真实证明mutation是整part重写,不是原地patch

做的事:300万行表,真实只更新其中1/3的status字段

代码CREATE TABLE demo.muttest (k UInt64, v String, status String) ENGINE=MergeTree ORDER BY k; INSERT INTO demo.muttest SELECT number, toString(number), 'old' FROM numbers(3000000); -- mutation前,part信息 SELECT name, bytes_on_disk FROM system.parts WHERE table='muttest' AND active; ALTER TABLE demo.muttest UPDATE status='new' WHERE k < 1000000;

验证:mutation是异步的,system.mutations真实能查到进度

实测 · 本机执行输出
ALTER TABLE demo.muttest UPDATE status='new' WHERE k < 1000000;
-- 语句立刻返回,真实不等mutation执行完

SELECT mutation_id, command, is_done, parts_to_do FROM system.mutations
WHERE table='muttest';
→ mutation_4.txt  UPDATE status='new' WHERE k<1000000  ...

-- 数据量不大,本机真实几乎瞬间就跑完了mutation,
-- 但语句本身返回和mutation真正完成,真实是两个独立的动作,
-- 用SETTINGS mutations_sync=1能强制客户端等mutation真正完成再返回

ALTER TABLE ... UPDATE/DELETE在ClickHouse里真实是一个异步任务(叫mutation),语句本身立刻返回,真正的数据重写在后台真实排队执行,可以通过system.mutations表的is_done列查询这个后台任务真实完成了没有。

验证:真实证据——只改了1/3数据,但整个part都被重写了

实测 · 本机执行输出(mutation前后part文件名和字节数对比)
mutation前:
  all_1_1_0    6404373 字节
  all_2_2_0    9107981 字节
  all_3_3_0    9173296 字节

mutation后(UPDATE status WHERE k < 1000000,只涉及全表1/3的行):
  all_1_1_0_4  6404373 字节   ← 真实文件名多了_4后缀,字节数几乎没变
  all_2_2_0_4  9107997 字节   ← 同样重写了,即使这个part可能完全不含k<1000000的行
  all_3_3_0_4  9173296 字节   ← 同样重写了
3个part全部真实都被重写成了带_4(对应这次mutation的版本号)后缀的新part,字节数几乎和mutation前一模一样(因为只是把status字段的值从'old'改成'new',字符串长度没变,其余数据原样照抄)——这正是mutation"很重"的真实原因:ClickHouse不支持像行式数据库那样对磁盘上的单行数据做原地patch,任何一个part只要有可能包含被WHERE条件命中的行,就会被整个读出来、按条件对每一行分别决定改不改、然后完整重新写一遍,哪怕这个part里只有一行真正需要修改,其余几十万行原样不变的数据也要跟着重写一次。这也解释了为什么官方建议高频更新的场景不要依赖ALTER UPDATE——本文300万行、只改1/3、本机本地SSD环境下几乎瞬间完成,但换成百亿行级别的真实生产表,一次覆盖大范围的mutation真实会读写巨量数据、占用大量磁盘IO和CPU,而且mutation的执行还会跟后台正常的merge任务真实抢占同一批后台线程资源。真正频繁变化的字段,更适合本系列后面会验证的Lightweight Delete,或者一开始就用CollapsingMergeTree/ReplacingMergeTree这类靠追加新行而不是原地改写来表达状态变化的设计。

分步演示:从异步mutation到part被整体重写的真实证据

每一步都是本文上面用真实ALTER TABLE UPDATE跑出来的原始结果。

只改1/3数据,真实整个part都被重写了
这一步新验证的状态

    本文用到的工具

    • 本机 ClickHouse 26.9.4(单节点),真实的 system.mutations / system.parts
    • 真实对比mutation前后part文件名与字节数,证明整part重写
    京ICP备2026067502号-1京公网安备11010502063487号