ClickHouse执行同步删除后插入的数据意外被删除问题
问题分析与解决方案
你的问题核心在于ReplicatedReplacingMergeTree引擎下,ALTER TABLE ... DELETE的异步特性与后续INSERT操作的时序冲突,导致新插入的数据被误删。以下是具体原因和解决办法:
核心原因
- ALTER DELETE本质是异步mutation:即使你认为是“同步删除”,ClickHouse的ALTER TABLE ... DELETE属于mutation操作,默认是后台异步执行的。即使客户端收到执行成功的响应,mutation可能还未在所有分区或副本上完成删除标记的应用。此时插入符合删除条件的新数据,后续合并操作会误将新数据匹配删除规则,导致被标记删除。
- ReplicatedReplacingMergeTree的无version逻辑:你的表未指定
version列,ReplacingMergeTree默认依赖内部插入顺序判断替换优先级,这种模糊的规则可能和mutation的删除标记产生冲突,加剧数据丢失的概率。
解决办法
1. 强制等待mutation完成后再插入
执行ALTER DELETE时,添加wait_for_mutation_finish=1参数,强制客户端等待mutation在所有副本上完全生效后再返回,之后再执行INSERT操作。
示例SQL:
SET wait_for_mutation_finish=1; ALTER TABLE my_table DELETE WHERE system = '你的条件值';
Java客户端实现时,需要先执行SET语句,再执行ALTER DELETE,确保操作同步完成后再执行INSERT。
2. 改用轻量级删除并等待异步完成
轻量级删除(DELETE FROM ... WHERE)虽然是异步的,但可以通过查询系统表等待其完成:
-- 执行轻量级删除 DELETE FROM my_table WHERE system = '你的条件值'; -- 循环查询直到异步任务完成 SELECT * FROM system.asynchronous_mutations WHERE table = 'my_table' AND query LIKE '%DELETE FROM my_table%' AND status = 'done';
确认任务状态为done后,再执行INSERT操作。
3. 优化表结构明确替换规则
给ReplicatedReplacingMergeTree添加version列,明确替换优先级,避免和删除标记的冲突:
CREATE TABLE IF NOT EXISTS my_table ( name String, surname String, system String, version UInt64 ) ENGINE = ReplicatedReplacingMergeTree(version) ORDER BY (system);
插入新数据时,给version设置比旧数据更大的值,确保新数据在合并时会保留,不会被误删。
4. 原子替换方案(推荐)
对于先删后插的场景,用临时表+原子操作避免中间状态问题:
- 创建临时表并插入新数据;
- 等待删除mutation完成后,将临时表的数据插入主表;
- 删除临时表。
示例:
-- 创建临时表 CREATE TABLE IF NOT EXISTS my_table_temp AS my_table ENGINE = MergeTree() ORDER BY (system); -- 插入新数据到临时表 INSERT INTO my_table_temp VALUES ('name1', 'surname1', 'system1'); -- 执行删除并等待完成 SET wait_for_mutation_finish=1; ALTER TABLE my_table DELETE WHERE system IN (SELECT system FROM my_table_temp); -- 插入新数据 INSERT INTO my_table SELECT * FROM my_table_temp; -- 清理临时表 DROP TABLE my_table_temp;
内容的提问来源于stack exchange,提问作者Sergey
相关产品推荐
相关产品推荐

