You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VersionedCollapsingMergeTree如何应对多客户端并发写入的逻辑错误?

VersionedCollapsingMergeTree并发更新问题及解决方案

根据ClickHouse文档《多线程实时更新》所述:

VersionedCollapsingMergeTree表在多客户端/多线程插入时实现去重十分便捷。

但它依赖**取消行(cancel row)**抵消历史行的机制,在并发场景下容易出现逻辑错误。比如有一张以id为主键的表,初始数据如下:

┌──id─┬─author──┬─views─┬─version─┐─sign─┐
│ 123 │ ricardo │   150 │    1    │   1  │
└─────┴─────────┴───────┴─────────┴──────┘

当两个客户端同时对该主键写入更新数据时:

client1:
┌──id─┬─author──┬─views─┬─version─┐─sign─┐
│ 123 │ ricardo │   150 │    1    │  -1  │
│ 123 │ ricardo │   200 │    2    │   1  │
└─────┴─────────┴───────┴────────────────┘

client2:
┌──id─┬─author──┬─views─┬─version─┐─sign─┐
│ 123 │ ricardo │   150 │    1    │  -1  │
│ 123 │ ricardo │   250 │    2    │   1  │
└─────┴─────────┴───────┴────────────────┘

初始的sign=1行会被两次抵消,最终残留两个sign=1的version=2行,导致数据失真。以下是几种可行的解决办法:

1. 使用全局唯一递增版本号

不要让客户端自行生成version,依赖全局递增的版本源(如分布式ID服务、ClickHouse序列表):

  • 创建序列表:
    CREATE TABLE version_sequence (id UInt64, next_version UInt64) ENGINE = ReplacingMergeTree PRIMARY KEY id;
    INSERT INTO version_sequence VALUES (1, 1);
    
  • 更新流程:先通过SELECT next_version FROM version_sequence WHERE id=1 FOR UPDATE获取当前版本,将版本号+1后更新回序列表,再用新生成的version创建取消行和新数据行,确保同一主键的版本号全局唯一且递增。

2. 加分布式锁控制并发

针对要更新的主键(如id=123),写入前先获取分布式锁,确保同一时间只有一个客户端能操作该主键:

  • 可借助外部锁服务(如Redis的SETNX命令),或利用ClickHouse的SELECT ... FOR UPDATE结合ReplicatedMergeTree特性实现行级锁,避免并发写入冲突。

3. 改用ReplacingMergeTree替代

若业务对实时性要求不高,可切换为ReplacingMergeTree,利用版本列自动替换旧数据:

  • 创建表语句示例:
    CREATE TABLE user_stats (
        id UInt64,
        author String,
        views UInt64,
        version UInt64
    ) ENGINE = ReplacingMergeTree(version)
    ORDER BY id;
    
  • 每次更新直接写入带更高version的新行即可,Merge操作会自动保留同一id下版本最高的行,无需手动生成取消行。

4. 统一汇总更新请求

将多客户端的更新请求汇总到中间服务,由该服务统一处理同一主键的更新,生成正确的取消行和新行后批量写入ClickHouse,从根源避免并发冲突。

内容的提问来源于stack exchange,提问作者Nick Allen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 13:14:56