跳至主要内容
跳至主要内容

轻量级 UPDATE 语句

Beta 功能。了解更多。
注意

轻量级更新目前处于 Beta 阶段。如果您遇到问题,请在 ClickHouse 仓库 中提交 issue。

轻量级的 UPDATE 语句更新表 [db.]table 中匹配表达式 filter_expr 的行。它被称为“轻量级更新”是为了与 ALTER TABLE ... UPDATE 查询形成对比,后者是一种重型操作,会重写数据部分中的整个列。它仅适用于 MergeTree 表引擎系列。

UPDATE [db.]table [ON CLUSTER cluster] SET column1 = expr1 [, ...] [IN PARTITION partition_expr] WHERE filter_expr;

filter_expr 必须是 UInt8 类型。此查询将指定列的值更新为对应表达式的值,这些表达式对应于 filter_expr 取非零值的行。值将使用 CAST 运算符转换为列类型。更新用于计算主键或分区键的列不受支持。

示例

UPDATE hits SET Title = 'Updated Title' WHERE EventDate = today();

UPDATE wikistat SET hits = hits + 1, time = now() WHERE path = 'ClickHouse';

轻量级更新不会立即更新数据

轻量级 UPDATE 使用 补丁部分 (patch parts) 实现 - 一种特殊的数据部分,仅包含更新的列和行。轻量级 UPDATE 创建补丁部分,但不会立即物理地修改存储中的原始数据。更新过程类似于 INSERT ... SELECT ... 查询,但 UPDATE 查询会等待补丁部分创建完成后才返回。

更新后的值是

  • 立即可见 通过应用补丁在 SELECT 查询中
  • 物理实现 仅在后续合并和变异期间
  • 自动清理 一旦所有活动部分都已实现补丁

轻量级更新要求

轻量级更新支持 MergeTreeReplacingMergeTreeCollapsingMergeTreeVersionedCollapsingMergeTree 引擎及其 ReplicatedShared 版本。

要使用轻量级更新,必须使用表设置 enable_block_number_columnenable_block_offset_column 启用 _block_number_block_offset 列的物化。

轻量级删除

可以作为轻量级 UPDATE 而不是 ALTER UPDATE 变异来运行 轻量级 DELETE 查询。轻量级 DELETE 的实现由设置 lightweight_delete_mode 控制。

性能考虑

轻量级更新的优点

  • 更新的延迟与 INSERT ... SELECT ... 查询的延迟相当
  • 仅写入更新的列和值,而不是数据部分中的整个列
  • 无需等待当前正在运行的合并/变异完成,因此更新的延迟是可预测的
  • 可以并行执行轻量级更新

潜在的性能影响

  • 为需要应用补丁的 SELECT 查询增加开销
  • 对于具有要应用补丁的数据部分的列,将不会使用 跳过索引。如果表存在补丁部分,即使对于没有要应用补丁的数据部分,也不会使用 投影
  • 过于频繁的小更新可能导致“太多部分”错误。建议将多个更新批处理到一个查询中,例如将更新的 ID 放在 WHERE 子句中的单个 IN 子句中
  • 轻量级更新旨在更新少量行(大约表的 10%)。如果您需要更新更大的数量,建议使用 ALTER TABLE ... UPDATE 变异

并发操作

与重型变异不同,轻量级更新不会等待当前正在运行的合并/变异完成。并发轻量级更新的一致性由设置 update_sequential_consistencyupdate_parallel_mode 控制。

更新权限

UPDATE 需要 ALTER UPDATE 权限。要在给定用户针对特定表启用 UPDATE 语句,请运行

GRANT ALTER UPDATE ON db.table TO username;

实现细节

补丁部分与常规部分相同,但仅包含更新的列和几个系统列

  • _part - 原始部分名称
  • _part_offset - 原始部分中的行号
  • _block_number - 原始部分中行的块号
  • _block_offset - 原始部分中块的偏移量
  • _data_version - 更新数据的版本(为 UPDATE 查询分配的块号)

平均而言,它为补丁部分中的每个更新行提供约 40 字节(未压缩数据)的开销。系统列有助于在原始部分中找到应更新的行。系统列与原始部分中的 虚拟列 相关联,如果应应用补丁部分,则会添加这些列。补丁部分按 _part_part_offset 排序。

补丁部分属于与原始部分不同的分区。补丁部分的分区 ID 为 patch-<补丁部分中列名的哈希值>-<原始分区 ID>。因此,具有不同列的补丁部分存储在不同的分区中。例如,三个更新 SET x = 1 WHERE <cond>SET y = 1 WHERE <cond>SET x = 1, y = 1 WHERE <cond> 将在三个不同的分区中创建三个补丁部分。

可以将补丁部分合并在一起,以减少 SELECT 查询中应用的补丁数量并减少开销。补丁部分的合并使用 替换 合并算法,并使用 _data_version 作为版本列。因此,补丁部分始终存储部分中每个更新行的最新版本。

轻量级更新不会等待当前正在运行的合并和变异完成,并始终使用数据部分的当前快照来执行更新并生成补丁部分。因此,可能存在两种应用补丁部分的情况

例如,如果读取部分 A,我们需要应用补丁部分 X

  • 如果 X 包含部分 A 本身。如果 A 在执行 UPDATE 时未参与合并,则会发生这种情况。
  • 如果 X 包含部分 BC,这些部分包含在部分 A 中。如果执行 UPDATE 时正在运行合并 (B, C) -> A,则会发生这种情况。

对于这两种情况,有两种应用补丁部分的方式

  • 使用按排序的列 _part_part_offset 合并。
  • 使用按 _block_number_block_offset 列进行连接。

连接模式比合并模式慢且需要更多内存,但使用频率较低。

    © . This site is unofficial and not affiliated with ClickHouse, Inc.