ClickHouse 数据一致性
本文及后续所有文章都以 21.7.3.14-2 做为版本讲解和入门学习
查询 CK 手册发现,即便对数据一致性支持最好的 Mergetree,也只是保证最终一致性。我们在使用 ReplacingMergeTree、SummingMergeTree 这类表引擎的时候,会出现短暂数据不一致的情况。在某些对一致性非常敏感的场景,通常有以下几种解决方案。
# 手动 OPTIMIZE(不建议)
在写入数据后,立刻执行 OPTIMIZE 强制触发新写入分区的合并动作。
OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]
# 通过 Group by 去重
可以根据自己实际重复的字段进行去重,然后对每个重复的组里选自己想要的数据。
select
user_id,
argMax(score, create_time) AS score,
argMax(deleted, create_time) AS deleted,
max(create_time) AS ctime
from a
group by user_id
# 分组完成后查询未被删除的数据
HAVING deleted = 0;
2
3
4
5
6
7
8
9
argMax (field1,field2) 按照 field2 的最大值取 field1 的值。
这种固定的查询语句我们可以提前封装为一个视图,以后只查视图就好了
CREATE VIEW view_a AS
SELECT
user_id ,
argMax(score, create_time) AS score,
argMax(deleted, create_time) AS deleted,
max(create_time) AS ctime
FROM a
GROUP BY user_id
HAVING deleted = 0;
2
3
4
5
6
7
8
9
查询视图
SELECT * FROM view_test_a WHERE user_id = 0;
# 通过 FINAL 查询
在查询语句后增加 FINAL 修饰符,这样在查询的过程中将会执行 Merge 的特殊逻辑(例如数据去重,预聚合等)。
但是这种方法在早期版本基本没有人使用,因为在增加 FINAL 之后,我们的查询将会变成一个单线程的执行过程,查询速度非常慢。在 v20.5.2.7-stable 版本中,FINAL 查询支持多线程执行,并且可以通过 max_final_threads 参数控制单个查询的线程数。但是目前读取 part 部分的动作依然是串行的。
FINAL 查询最终的性能和很多因素相关,列字段的大小、分区的数量等等都会影响到最终的查询时间,所以还要结合实际场景取舍。final 只能在部分表引擎中使用。
语法
# 普通查询
select * from visits_v1 WHERE StartDate = '2014-03-17' limit 100 settings max_threads = 2;
# final 查询
select * from visits_v1 final WHERE StartDate = '2014-03-17' limit 100 settings max_final_threads = 2;
2
3
4
5
可以通过查看以上语法的执行计划,会发现使用 final 关键字后,在分区的的数据查询时,会是单线程执行,即使设置了线程数为 2.
最终使用哪种方式保证数据一致性,可以用第 2 或第 3 种对已有的数据进行测试后在决定,在不同的数据量,不同的情况,两者性能存在着差异。