Mutation之痛

虽然在OLAP用例中很少见,但有时修改数据是不可避免的。为了满足这一需求,云数据库ClickHouse提供了Mutation功能,允许用户通过ALTER修改已插入的数据,本文为您介绍Mutation功能、使用时的注意事项以及可能产生的影响。

了解Mutation

功能介绍

  • 定义:通过 ALTER TABLE 语句(如 DELETEUPDATE 或 ALTER COLUMN)修改已插入的数据。

  • 实现原理:

    • 重写数据块:Mutation 需扫描并重写所有相关data part,生成新data part替换旧data part

    • 共享资源:与数据合并(Merge)共用线程池,可能导致两者互相抢占资源。

    以下为Mutation的原理流程图。图片

  • 默认行为:

    全副本生效:由于分布式表的同步机制要求,Mutation需在所有副本上执行,这会显著加重集群资源负担。

  • 适用场景:必须修改历史数据的极端场景(如修复错误或删除敏感信息)。

注意事项

Mutation操作影响较多,建议严格限制并仅由管理员执行。

影响

  • CPU/IO 饱和:大规模Mutation会消耗大量计算和磁盘资源,拖慢其他任务(如查询、合并)。

  • 分布式压力:在副本节点上并行执行时,可能导致集群整体性能下降。

  • 合并堆积:Mutation占用合并线程池,导致正常合并任务积压,引发Too many parts错误。

  • 复制延迟:分布式表的副本需同步Mutation,可能因节点性能不足导致数据不一致。

  • 无法回滚:Mutation执行后即使取消,已处理的data part已被修改。

  • 内存资源压力:大规模Mutation也会消耗大量内存资源,导致查询因为内存使用超限被取消。

使用场景及替代方案

场景一:数据去重

当您有重复的数据需要执行合并时,建议您在插入到ClickHouse之前的上游解决这个问题。Mutation可用于数据去重,基于这种场景,可采用以下替代方案。

  • 在查询时去重。

    在查询时去重可以通过对唯一字段的行进行group by,并使用argMax函数和日期字段来确定其他字段的最新值。

  • 使用ReplacingMergeTree去重。

    ReplacingMergeTree允许具有相同排序键(ORDER BY键)的行在合并时去重。此操作只是尽可能地去重,但并不能保证完全去重。这是由于执行合并的线程在不同时间执行所导致的,因此data part的合并时间也无法确定,从而无法保证没有重复数据的出现。

    重要

    该功能仅适用于MergeTree系列引擎。更多详情,请参见ReplacingMergeTree

  • 使用FINAL去重。

    您还可以使用FINAL修饰符在SELECT时强制合并data part,进而达到去重目的。

    重要

    此操作是资源密集型的,您需谨慎使用。

  • OPTIMIZE FINAL在磁盘上强制合并。

    重要

    OPTIMIZE FINAL强制将表的所有data part合并成一个单一的data part,执行时会消耗大量系统资源。

场景二:删除数据

在需要删除数据的情况下,Mutation也可用于删除数据。建议您使用lightweight deletes代替Mutation。在大多数情况下,它比使用Mutation效率更高,除非您正在进行大规模批量删除数据。

  • 如果您的集群版本大于等于23.8,lightweight deletes功能默认已启用,其参数为enable_lightweight_delete,您无需手动开启。

  • 如果您的集群版本小于23.8,您需手动开启lightweight deletes功能,即设置SET allow_experimental_lightweight_delete = true;

lightweight deletes核心工作机制是标记删除而非立即删除。即执行DELETE语句时,符合条件的行会被标记为删除,这些标记在查询时用于过滤行,并在parts合并时被删除。大致流程如下图所示。

图片