Clickhouse ReplacingMergeTree存储规则及自定义建表合理性咨询
1. ClickHouse是否会按主键存储ReplacingMergeTree表的数据?
不会。MergeTree系列引擎(包括ReplacingMergeTree)始终按ORDER BY定义的排序键来组织存储数据,PRIMARY KEY的作用是生成稀疏索引以加速查询,而非决定数据的物理存储顺序。如果不显式指定PRIMARY KEY,ClickHouse会默认使用ORDER BY的键作为主键;但如果显式指定了不同的主键,存储顺序依然由ORDER BY控制。
2. 你的建表语句是否满足需求?
先拆解你的两个核心需求:
- 需求1:插入相同
Id和Code的行时,更新现有数据(替换重复条目) - 需求2:数据物理存储时按
DownloadedAt排序
对需求1的满足情况
你的建表语句中ORDER BY (Id, Code),而ReplacingMergeTree的去重逻辑是基于ORDER BY键的——当合并分区时,相同ORDER BY键的行会被保留最后一条(默认按写入顺序,若指定版本列则保留版本值最大的行)。这部分是符合需求1的:相同Id+Code的行在合并后会被替换,达到“更新”的效果。
对需求2的满足情况
不满足。如前所述,数据的物理存储顺序由ORDER BY决定,你的ORDER BY是(Id, Code),所以数据会先按Id排序,再按Code排序,不会按DownloadedAt排序存储。你指定的PRIMARY KEY (Id, Code, DownloadedAt)只会让稀疏索引包含DownloadedAt列,能加速按DownloadedAt的查询,但改变不了物理存储的顺序。
是否可以这样建表?
语法上是合法的,但它无法满足你“按DownloadedAt物理排序存储”的需求。如果要兼顾两个需求,需要做调整:
- 若你希望保留最新
DownloadedAt的行作为更新结果,可以将DownloadedAt设为ReplacingMergeTree的版本列,修改建表语句:
CREATE TABLE products ON CLUSTER cluster ( Id UUID, Code String, DownloadedAt DateTime, -- 其他列 ) ENGINE = ReplicatedReplacingMergeTree(DownloadedAt) -- 指定版本列 ORDER BY (Id, Code) PRIMARY KEY (Id, Code, DownloadedAt);
这样合并时会自动保留DownloadedAt最大的行,同时存储顺序仍按Id+Code,但查询时如果需要按DownloadedAt排序,可以直接加ORDER BY DownloadedAt,配合主键索引也能高效执行。
- 若你必须让数据物理存储时按
DownloadedAt排序,那ORDER BY需要包含DownloadedAt,但此时ReplacingMergeTree的去重键会变成(Id, Code, DownloadedAt)——这意味着相同Id+Code但DownloadedAt不同的行不会被替换,直接违背你的更新需求,这种场景下建议重新评估业务逻辑,或考虑其他引擎(如AggregatingMergeTree)。
内容的提问来源于stack exchange,提问作者AlexAlum

