轻量级 UPDATE 语句
轻量级更新目前处于测试阶段。 如果遇到问题,请在 ClickHouse 仓库 中提起问题。
轻量级 UPDATE 语句更新与表达式 filter_expr 匹配的表 [db.]table 中的行。
之所以称为“轻量级更新”,是为了与 ALTER TABLE ... UPDATE 查询进行对比,后者是一个重型过程,重写数据部分中的整个列。
它仅适用于 MergeTree 表引擎系列。
filter_expr 必须为 UInt8 类型。该查询将指定列的值更新为 filter_expr 值为非零值的行中对应表达式的值。
使用 CAST 操作符将值转换为列类型。更新用于计算主键或分区键的列是不支持的。
示例
轻量级更新不会立即更新数据
轻量级 UPDATE 是通过 patch parts 实现的 - 一种仅包含更新列和行的特殊数据部分。
轻量级 UPDATE 创建补丁部分,但不会立即在存储中物理修改原始数据。
更新过程类似于 INSERT ... SELECT ... 查询,但 UPDATE 查询在返回之前等待补丁部分创建完成。
更新的值是:
- 立即可见 通过补丁应用的
SELECT查询 - 物理具现 仅在后续的合并和变更过程中
- 自动清理 一旦所有活动部分已经应用补丁
轻量级更新要求
轻量级更新支持 MergeTree、ReplacingMergeTree、CollapsingMergeTree 引擎及其 Replicated 和 Shared 版本。
要使用轻量级更新,必须通过表设置 enable_block_number_column 和 enable_block_offset_column 启用 _block_number 和 _block_offset 列的具现化。
轻量级删除
可以将 轻量级 DELETE 查询作为轻量级 UPDATE 运行,而不是 ALTER UPDATE 变更。轻量级 DELETE 的实现由设置 lightweight_delete_mode 控制。
性能考虑
轻量级更新的优点:
- 更新的延迟与
INSERT ... SELECT ...查询的延迟相当 - 仅写入更新的列和值,而不是数据部分中的整个列
- 无需等待当前正在运行的合并/变更完成,因此更新的延迟是可预测的
- 可能并行执行轻量级更新
潜在的性能影响:
- 为需要应用补丁的
SELECT查询添加额外开销 - 跳过索引 将不适用于数据部分中需要应用补丁的列。如果表中存在补丁部分,则 投影 将不被使用,包括数据部分中没有需要应用补丁的部分。
- 频繁的小更新可能导致“部分过多”错误。建议将多个更新批量成一个单独的查询,例如将更新的 ids 放在
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本身。发生这种情况是因为当执行UPDATE时,A并未参与合并。 - 如果
X包含部分B和C,它们被部分A覆盖。这种情况发生在执行UPDATE时有一个合并(B、C)->A正在运行。
对于这两种情况,分别有两种方法应用补丁部分:
- 按照已排序列
_part、_part_offset使用合并。 - 按照
_block_number、_block_offset列使用连接。
连接模式比合并模式慢,并需要更多内存,但使用频率较低。
相关内容
ALTER UPDATE- 重型UPDATE操作- 轻量级
DELETE- 轻量级DELETE操作