轻量级删除
轻量级 DELETE
语句从表 [db.]table
中删除与表达式 expr
匹配的行。它仅适用于 *MergeTree 表引擎系列。
DELETE FROM [db.]table [ON CLUSTER cluster] [IN PARTITION partition_expr] WHERE expr;
它被称为“轻量级 DELETE
”以区别于 ALTER TABLE ... DELETE 命令,该命令是一个重量级进程。
示例
-- Deletes all rows from the `hits` table where the `Title` column contains the text `hello`
DELETE FROM hits WHERE Title LIKE '%hello%';
轻量级 DELETE
不会立即删除数据
轻量级 DELETE
被实现为一个 操作,它将行标记为已删除,但不会立即物理删除它们。
默认情况下,DELETE
语句会在将行标记为已删除完成后才返回。如果数据量很大,这可能需要很长时间。或者,您可以使用设置 lightweight_deletes_sync
在后台异步运行它。如果禁用,DELETE
语句将立即返回,但数据可能仍然对查询可见,直到后台操作完成。
该操作不会物理删除已被标记为已删除的行,这将仅在下次合并期间发生。因此,在一段未指定的时间内,数据实际上并没有从存储中删除,只是被标记为已删除。
如果您需要确保数据在可预测的时间内从存储中删除,请考虑使用表设置 min_age_to_force_merge_seconds
。或者,您可以使用 ALTER TABLE ... DELETE 命令。请注意,使用 ALTER TABLE ... DELETE
删除数据可能会消耗大量资源,因为它会重新创建所有受影响的部分。
删除大量数据
大量删除会对 ClickHouse 性能产生负面影响。如果您试图从表中删除所有行,请考虑使用 TRUNCATE TABLE
命令。
如果您预计会频繁删除,请考虑使用 自定义分区键。然后,您可以使用 ALTER TABLE ... DROP PARTITION
命令快速删除与该分区相关联的所有行。
轻量级 DELETE
的限制
带有投影的轻量级 DELETE
默认情况下,DELETE
不适用于带有投影的表。这是因为投影中的行可能会受到 DELETE
操作的影响。但是有一个 MergeTree 设置 lightweight_mutation_projection_mode
来更改行为。
使用轻量级 DELETE
时的性能注意事项
使用轻量级 DELETE
语句删除大量数据可能会对 SELECT 查询性能产生负面影响。
以下内容也会对轻量级 DELETE
性能产生负面影响
DELETE
查询中繁重的WHERE
条件。- 如果操作队列中填充了太多其他操作,这可能会导致性能问题,因为表上的所有操作都是按顺序执行的。
- 受影响的表具有非常多的数据部分。
- 在压缩部分中有很多数据。在压缩部分中,所有列都存储在一个文件中。
删除权限
DELETE
需要 ALTER DELETE
权限。要为给定用户在特定表上启用 DELETE
语句,请运行以下命令
GRANT ALTER DELETE ON db.table to username;
轻量级 DELETE 在 ClickHouse 中的内部工作原理
将“掩码”应用于受影响的行
当执行
DELETE FROM table ...
查询时,ClickHouse 会保存一个掩码,其中每行都被标记为“存在”或“已删除”。后续查询会忽略这些“已删除”的行。但是,行实际上是在后续合并中删除的。写入此掩码比ALTER TABLE ... DELETE
查询所做的要轻量级得多。该掩码被实现为一个隐藏的
_row_exists
系统列,它为所有可见行存储True
,为已删除的行存储False
。如果部分中有一些行被删除,则该列仅存在于该部分中。如果部分中的所有值都等于True
,则该列不存在。SELECT
查询被转换为包含掩码当查询中使用掩码列时,
SELECT ... FROM table WHERE condition
查询在内部通过_row_exists
上的谓词进行扩展,并转换为SELECT ... FROM table PREWHERE _row_exists WHERE condition
在执行时,将读取
_row_exists
列以确定哪些行不应返回。如果存在许多已删除的行,ClickHouse 可以确定在读取其余列时可以完全跳过哪些颗粒。DELETE
查询被转换为ALTER TABLE ... UPDATE
查询DELETE FROM table WHERE condition
被转换为ALTER TABLE table UPDATE _row_exists = 0 WHERE condition
操作。在内部,此操作分为两个步骤执行
对每个单独的部分执行
SELECT count() FROM table WHERE condition
命令以确定该部分是否受影响。根据上面的命令,然后对受影响的部分进行操作,并为不受影响的部分创建硬链接。在宽部分的情况下,将更新每行的
_row_exists
列,并且所有其他列的文件将被硬链接。对于压缩部分,所有列都被重新写入,因为它们都存储在一个文件中。
从上面的步骤可以看出,使用掩码技术的轻量级
DELETE
比传统的ALTER TABLE ... DELETE
提高了性能,因为它不会对受影响的部分重新写入所有列的文件。