ClickHouse哈希分片键场景下跨分片数据重复原因咨询
我通过Altinity Kubernetes ClickHouse Operator部署了一个包含4个分片、2个副本的分布式ClickHouse集群,相关DDL语句如下:
创建数据库
CREATE DATABASE my_db ON cluster 'my-cluster'
创建本地表
CREATE TABLE my_db.my_table_local ON cluster 'my-cluster' ( `time` DateTime('UTC'), `id` String, `type` LowCardinality(String), `value` Nullable(Float32) ) ENGINE = ReplicatedReplacingMergeTree('/clickhouse/my_db/tables/{shard}/my_table_local', '{replica}') ORDER BY (id, type, time) PARTITION BY toStartOfMonth(time) SETTINGS min_age_to_force_merge_seconds = 60;
创建分布式表
CREATE TABLE my_db.my_table ON cluster 'my-cluster' AS my_db.my_table_local ENGINE Distributed('my-cluster', my_db, my_table_local, cityHash64(id));
所有插入操作均针对分布式表my_db.my_table执行。集群采用K8s Operator部署,每个服务器实例/副本运行在独立Pod中,同时部署了3个ClickHouse-Keeper实例。
目前遇到的问题:偶尔会出现跨分片的数据重复,且这些重复数据会一直存在(已知ReplacingMergeTree无法跨分片去重,且分片数量从未变更)。此前已针对集群故障容错、复制机制做过压力测试(如强制终止服务器及ClickHouse-Keeper节点),了解过非活跃/只读副本的处理方法。
疑问:这种跨分片重复为何会发生?按理解,哈希函数作为分片键应能将插入请求一致路由到同一分片,是否存在例外情况(如分片不可用、ClickHouse-Keeper分片信息损坏等)?
分片临时不可用时的写入fallback:当分布式表路由的目标分片所有副本都不可用时,ClickHouse会触发fallback机制,将写入请求转发到集群中其他可用的分片。这种情况下,原本应该落在某分片的数据会被写入其他分片,后续目标分片恢复后,同一
id的数据就会分布在多个分片上,导致跨分片重复。可通过查看ClickHouse日志(搜索fallback关键词)确认是否发生过这种情况。分布式表拓扑缓存不一致:分布式表会缓存集群的分片拓扑信息,如果某个节点的缓存未及时更新(比如K8s Pod重启后缓存未同步,或是ClickHouse-Keeper的拓扑变更通知延迟),可能导致不同节点对分片路由的计算出现差异,同一
id被路由到不同分片。可执行SYSTEM RELOAD DISTRIBUTED METADATA手动刷新缓存,观察是否还会出现重复。客户端重试无幂等控制:如果客户端在写入时遇到超时或报错自行重试,但未做幂等处理,且第一次写入其实已经成功(只是客户端未收到响应),重试时目标分片可能已经恢复,或是重试请求被路由到其他分片(比如第一次写入触发了fallback,重试时目标分片可用),就会导致同一数据被写入多个分片。需要检查客户端的重试逻辑是否使用
INSERT ... WITH idempotency_key语法做幂等控制。ClickHouse-Keeper脑裂:虽然部署了3个ClickHouse-Keeper实例,但如果出现网络分区导致脑裂,部分ClickHouse节点可能获取到错误的分片拓扑信息,进而将数据路由到错误的分片。可查看ClickHouse-Keeper的日志,确认是否出现过节点失联、选举异常等情况。
分片键计算存在差异:极端情况下,如果不同节点的
cityHash64实现存在差异(比如不同ClickHouse版本的哈希函数更新),或是id字段存在隐式类型转换差异(比如部分节点将id视为String,部分因配置问题解析为其他类型),会导致同一id计算出的哈希值不同,从而路由到不同分片。需确认所有ClickHouse节点版本一致,且表结构的字段类型定义完全统一。
内容的提问来源于stack exchange,提问作者comaes

