轻量级 UPDATE 语句
轻量级更新目前处于 Beta 阶段。如果您遇到问题,请在 ClickHouse 仓库 中提交 issue。
轻量级的 UPDATE 语句更新表 [db.]table 中匹配表达式 filter_expr 的行。它被称为“轻量级更新”是为了与 ALTER TABLE ... UPDATE 查询形成对比,后者是一种重型操作,会重写数据部分中的整个列。它仅适用于 MergeTree 表引擎系列。
filter_expr 必须是 UInt8 类型。此查询将指定列的值更新为对应表达式的值,这些表达式对应于 filter_expr 取非零值的行。值将使用 CAST 运算符转换为列类型。更新用于计算主键或分区键的列不受支持。
示例
轻量级更新不会立即更新数据
轻量级 UPDATE 使用 补丁部分 (patch parts) 实现 - 一种特殊的数据部分,仅包含更新的列和行。轻量级 UPDATE 创建补丁部分,但不会立即物理地修改存储中的原始数据。更新过程类似于 INSERT ... SELECT ... 查询,但 UPDATE 查询会等待补丁部分创建完成后才返回。
更新后的值是
- 立即可见 通过应用补丁在
SELECT查询中 - 物理实现 仅在后续合并和变异期间
- 自动清理 一旦所有活动部分都已实现补丁
轻量级更新要求
轻量级更新支持 MergeTree、ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree 引擎及其 Replicated 和 Shared 版本。
要使用轻量级更新,必须使用表设置 enable_block_number_column 和 enable_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_consistency 和 update_parallel_mode 控制。
更新权限
UPDATE 需要 ALTER UPDATE 权限。要在给定用户针对特定表启用 UPDATE 语句,请运行
实现细节
补丁部分与常规部分相同,但仅包含更新的列和几个系统列
_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包含部分B和C,这些部分包含在部分A中。如果执行UPDATE时正在运行合并 (B,C) ->A,则会发生这种情况。
对于这两种情况,有两种应用补丁部分的方式
- 使用按排序的列
_part、_part_offset合并。 - 使用按
_block_number、_block_offset列进行连接。
连接模式比合并模式慢且需要更多内存,但使用频率较低。
相关内容
ALTER UPDATE- 重型UPDATE操作- 轻量级
DELETE- 轻量级DELETE操作 APPLY PATCHES- 强制将补丁物理化到数据部分(变异操作)