和之前的传统关系型数据库不同,clickhouse的主键不具有唯一性约束,那如果有一些需要数据去重的场景我们该咋办呢?别急,我们通过问答的方式来了解一下吧~~
Q:为什么clickhouse在设计的时候主键不具有唯一性约束呢?
A: 坦白讲,我也不清楚,因为我没看到过对此有官方的说明,我的理解是因为clickhouse采用的是稀疏索引,如果在插入的时候还要进行唯一性检查,将会额外的耗费一些时间,不像使用稠密索引的方式能够更快定位到重复,毕竟clickhouse的设计理念是为了一个“快”字,而且使用的场景更多的也是OLAP分析,因此不做去重检查也可以理解。
Q:如果主键没有唯一性约束,那它还有什么用处呢?
A: 用来建索引啊,快速查询就全靠它了。
Q:那我确实有些应用场景不希望包含重复的数据,咋整??
A: clickhouse给你想了一个办法,就是建表的时候使用ReplacingMergeTree,也是MergeTree引擎的一种,它会在你合并分区数据的时候,按照指定的key分组,然后取组里的最后一条记录。
Q:好,可是按照什么方式分组呢?按照建表时设定的主键吗?
A: No,no,不是按照主键,而是按照order by的排序键,当然大部分情况下我们会把主键和排序键设成一样的。比如order by (id,code),那么执行optimize手动触发合并的时候,同一分区内相同(id,code)的记录将会被合并,只保留最后一条。
Q: 可是,如果我不想保留最后一条呢?能否我自定义去重策略呢?
A: 答案是可以的,在使用Engine=ReplaceMergeTree(ver)的时候可以指定任意一个字段作为version,如果ver为空,则默认就是合并取最后一条,如果指定了的话,那么就是取组内ver字段取值最大的一条。
Q: 等等,你刚才一直强调说同一分区内?难道这个去重只是针对同一分区内的数据吗?
A: 你说对了,这个ReplaceMergeTree是以分区为单位删除重复数据的,如果不在同一个分区内则不会删除。所以在使用该引擎的时候,一定要考虑好分区键的设置,然后看看能不能满足你们的需求,不要盲目使用,比如你的场景只是在同一天内不允许出现重复的数据,那么就可以按照天来进行分区,然后使用该方法。
如果还有其它更好的全表去重的方法,欢迎留言交流~~