ClickHouse高级数据一致性(重点)完整使用 第九章

查询 CK 手册发现,即便对数据一致性支持最好的 Mergetree,也只是保证最终一致性:
我们在使用 ReplacingMergeTree、SummingMergeTree 这类表引擎的时候,会出现短暂数据不一致的情况。

在某些对一致性非常敏感的场景,通常有以下几种解决方案。

在这里插入图片描述

一、准备测试表和数据

(1)创建表

CREATE TABLE test_a(
 user_id UInt64,
 score String,
 deleted UInt8 DEFAULT 0,
 create_time DateTime DEFAULT toDateTime(0)
)ENGINE= ReplacingMergeTree(create_time)
ORDER BY user_id;
其中:
user_id 是数据去重更新的标识;
create_time 是版本号字段,每组数据中 create_time 最大的一行表示最新的数据;
deleted 是自定的一个标记位,比如 0 代表未删除,1 代表删除数据。

(2)写入 1000 万 测试数据

INSERT INTO TABLE test_a(user_id,score)
WITH(
 SELECT ['A','B','C','D','E','F','G']
)AS dict
SELECT number AS user_id, dict[number%7+1] FROM numbers(10000000);

(3)修改前 50 万 行数据,修改内容包括 name 字段和 create_time 版本号字段

INSERT INTO TABLE test_a(user_id,score,create_time)
WITH(
 SELECT ['AA','BB','CC','DD','EE','FF','GG']
)AS dict
SELECT number AS user_id, dict[number%7+1], now() AS create_time FROM 
numbers(500000);

(4)统计总数

SELECT COUNT() FROM test_a;

10500000

还未触发分区合并,所以还未去重

二、手动 OPTIMIZE

在写入数据后,立刻执行 OPTIMIZE 强制触发新写入分区的合并动作。

OPTIMIZE TABLE test_a FINAL;
语法:OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | 
PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]

三、通过 Group by 去重

(1)执行去重的查询

 user_id ,
 argMax(score, create_time) AS score, 
 argMax(deleted, create_time) AS deleted,
 max(create_time) AS ctime 
FROM test_a 
GROUP BY user_id
HAVING deleted = 0;

函数说明:

◼ argMax(field1,field2):按照 field2 的最大值取 field1 的值。

当我们更新数据时,会写入一行新的数据,例如上面语句中,通过查询最大的
create_time 得到修改后的 score 字段值。

(2)创建视图,方便测试

CREATE VIEW view_test_a AS
SELECT
 user_id ,
 argMax(score, create_time) AS score, 
 argMax(deleted, create_time) AS deleted,
 max(create_time) AS ctime 
FROM test_a 
GROUP BY user_id
HAVING deleted = 0;

(3)插入重复数据,再次查询

#再次插入一条数据
INSERT INTO TABLE test_a(user_id,score,create_time)
VALUES(0,'AAAA',now())
#再次查询
SELECT *
FROM view_test_a
WHERE user_id = 0;

(4)删除数据测试

#再次插入一条标记为删除的数据
INSERT INTO TABLE test_a(user_id,score,deleted,create_time) 
VALUES(0,'AAAA',1,now());
#再次查询,刚才那条数据看不到了
SELECT * FROM view_test_a WHERE user_id = 0;

这行数据并没有被真正的删除,而是被过滤掉了。在一些合适的场景下,可以结合 表级别的 TTL 最终将物理数据删除。

四、通过 FINAL 查询

在查询语句后增加 FINAL 修饰符,这样在查询的过程中将会执行 Merge 的特殊逻辑(例
如数据去重,预聚合等)。

但是这种方法在早期版本基本没有人使用,因为在增加 FINAL 之后,我们的查询将会变
成一个单线程的执行过程,查询速度非常慢。

在 v20.5.2.7-stable 版本中,FINAL 查询支持多线程执行,并且可以通过 max_final_threads
参数控制单个查询的线程数。但是目前读取 part 部分的动作依然是串行的。

FINAL 查询最终的性能和很多因素相关,列字段的大小、分区的数量等等都会影响到最
终的查询时间,所以还要结合实际场景取舍。

参考链接:https://github.com/ClickHouse/ClickHouse/pull/10463
使用 hits_v1 表进行测试:
分别安装了 20.4.5.36 和 21.7.3.14 两个版本的 ClickHouse 进行对比。

1、老版本测试

(1)普通查询语句

select * from visits_v1 WHERE StartDate = '2014-03-17' limit 100;

(2)FINAL 查询

select * from visits_v1 FINAL WHERE StartDate = '2014-03-17' limit 100;

先前的并行查询变成了单线程。

2、新版本测试

(1)普通语句查询

select * from visits_v1 WHERE StartDate = '2014-03-17' limit 100 settings max_threads = 2;

查看执行计划:

explain pipeline select * from visits_v1 WHERE StartDate = '2014-03-17'
limit 100 settings max_threads = 2;

(Expression) 
ExpressionTransform × 2 
 (SettingQuotaAndLimits) 
 (Limit) 
 Limit 22 
 (ReadFromMergeTree)
 MergeTreeThread × 2 01

明显将由 2 个线程并行读取 part 查询。

(2)FINAL 查询

select * from visits_v1 final WHERE StartDate = '2014-03-17' limit 100 settings max_final_threads = 2;

查询速度没有普通的查询快,但是相比之前已经有了一些提升,查看 FINAL 查询的执行
计划:

explain pipeline select * from visits_v1 final WHERE StartDate = '2014-
03-17' limit 100 settings max_final_threads = 2;

(Expression) 
ExpressionTransform × 2 
 (SettingQuotaAndLimits) 
 (Limit) 
 Limit 22 
 (ReadFromMergeTree) 
 ExpressionTransform × 2 
 CollapsingSortedTransform × 2
 Copy 12 
 AddingSelector 
 ExpressionTransform 
 MergeTree 01

从 CollapsingSortedTransform 这一步开始已经是多线程执行,但是读取 part 部分的动
作还是串行

五、总结

数据一致性: replacingMergeTree不能保证查询时没重复、只能保证最终一致性

解决方案:
   1、手动执行:生产环境不推荐
   2、通过SQL实现去重:group by ==> 高级一点用法,加标记字段
   3、使用final:
   		20.5 之前的版本、final是单线程
   		20.5 之后的版本,final可以是多线程,但是读取part是串行的
   4、重复一点无所谓:100万日活,统计出来1001
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ClickHouse 是一个列式数据库管理系统,用于存储和处理大规模数据。在写入 ClickHouse 数据时,可能会涉及到数据一致性问题。 ClickHouse 默认情况下是可扩展的,它使用主从复制机制提供高可用性。在数据写入时,首先将数据写入主节点,然后主节点将数据进行复制并同步到所有从节点上。这确保了数据的冗余备份和高可用性。 然而,由于主从复制需要一定的时间,从节点可能会稍有延迟。因此,从节点上的数据可能会略微滞后于主节点上的数据。这意味着当数据写入完成后,立刻从从节点上查询可能无法得到最新的结果。 为了解决这一问题,ClickHouse 提供了两个级别的一致性保证: 1. 弱一致性(Eventual Consistency):这是 ClickHouse 的默认行为。在写入后的瞬间,从节点可能会滞后于主节点,但之后它们会趋向于一致。这种滞后是由于数据在网络上的传输延迟造成的。这种弱一致性可以满足大多数应用场景的需求,尤其是大规模数据分析和实时报表生成。 2. 强一致性(Strong Consistency):ClickHouse 也提供了强一致性的选项,可以通过配置来确保主节点和从节点的数据保持一致。使用这种模式会引入一定的性能开销,因为主节点会在每次写入操作完成后等待所有从节点进行数据复制和同步。 在实际应用中,根据具体的业务需求和对数据一致性的要求,可以选择适合的一致性级别。弱一致性ClickHouse 的默认模式,适用于大多数场景。而如果对数据实时一致性和精确性要求很高,可以选择强一致性模式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值