查询CK手册发现,即便对数据一致性支持最好的Mergetree,也只是保证最终一致性:
我们在使用ReplacingMergeTree、SummingMergeTree这类表引擎的时候,会出现短暂数据不一致的情况。
在某些对一致性非常敏感的场景,通常有以下几种解决方案。
1准备测试表和数据
(1)创建表
CREATETABLEtest_a(
user_idUInt64,
scoreString,
deletedUInt8DEFAULT0,
create_timeDateTimeDEFAULTtoDateTime(0)
)ENGINE=ReplacingMergeTree(create_time)
ORDERBYuser_id;
其中:
user_id是数据去重更新的标识;
create_time是版本号字段,每组数据中create_time最大的一行表示最新的数据;
deleted是自定的一个标记位,比如0代表未删除,1代表删除数据。
(2)写入1000万测试数据
INSERTINTOTABLE test_a(user_id,score)
WITH(
SELECT服务器托管网 ['A','B','C','D','E','F','G']
)ASdict
SELECTnumberASuser_id,dict[number%7+1]FROMnumbers(10000000);
(3)修改前50万行数据,修改内容包括name字段和create_time版本号字段
INSERTINTOTABLE test_a(user_id,score,create_time)
WITH(
SELECT ['AA','BB','CC','DD','EE','FF','GG']
)ASdict
SELECTnumberASuser_id,dict[number%7+1], now()AScreate_timeFROMnumbers(500000);
(4)统计总数
SELECT COUNT()FROMtest_a;
10500000
还未触发分区合并,所以还未去重。
2手动OPTIMIZE
在写入数据后,立刻执行OPTIMIZE强制触发新写入分区的合并动作。
OPTIMIZETABLEtest_aFINAL;
语法:OPTIMIZETABLE [db.]name[ONCLUSTERcluster] [PARTITIONpartition|PARTITIONID'partition_id'] [FINAL] [DEDUPLICATE[BYexpression]]
3通过Group by 去重
(1)执行去重的查询
SELECT
user_id,
argMax(score,create_time)ASscore,
argMax(deleted,create_time)ASdeleted,
max(create_time)ASctime
FROMtest_a
GROUP BYuser_id
HAVINGdeleted= 0;
函数说明:
- argMax(field1,field2):按照field2的最大值取field1的值。
当我们更新数据时,会写入一行新的数据,例如上面语句中,通过查询最大的create_time得到修改后的score字段值。
(2)创建视图,方便测试
CREATEVIEWview_test_aAS
SELECT
user_id,
argMax(score,create_time)ASscore,
argMax(deleted,create_time)ASdeleted,
max(create_time)ASctime
FROMtest_a
GROUP BYuser_id
HAVINGdeleted= 0;
(3)插入重复数据,再次查询
#再次插入一条数据
INSERTINTOTABLE test_a(user_id,score,create_time) VALUES(0,'AAAA',now())
#再次查询
SELECT *
FROMview_test_a
WHEREuser_id= 0;
(4)删除数据测试
#再次插入一条标记为删除的数据
INSERTINTOTABLE test_a(user_id,score,deleted,create_time) VALUES(0,'AAAA',1,now());
#再次查询,刚才那条数据看不到了
SELECT *
FROMview_test_a
WHEREuser_id= 0;
这行数据并没有被真正的删除,而是被过滤掉了。在一些合适的场景下,可以结合表级别的TTL最终将物理数据删除。
4通过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进行对比。
4.1老版本测试
(1)普通查询语句
select*fromvisits_v1WHEREStartDate=’2014-03-17’limit100;
(2)FINAL查询
select*fromvisits_v1FINALWHEREStartDate=’2014-03-17’limit100;
先前的并行查询变成了单线程。
4.2新版本测试
(1)普通语句查询
select*fromvisits_v1WHEREStartDate=’2014-03-17’limit100settingsmax_threads=2;
查看执行计划:
explainpipelineselect*fromvisits_v1WHEREStartDate=’2014-03-17’limit100settingsmax_threads=2;
(Expression)
ExpressionTransform2
(SettingQuotaAndLimits)
(Limit)
Limit2→2
(ReadFromMergeTree)
MergeTreeThread20→1
明显将由2个线程并行读取part查询。
(2)FINAL查询
select*fromvisits_v1finalWHEREStartDate=’2014-03-17’limit100settingsmax_final_threads=2;
查询速度没有普通的查询快,但是相比之前已经有了一些提升,查看FINAL查询的执行计划:
explainpipelineselect*fromvisits_v1finalWHEREStartDate=’2014-03-17’limit100settingsmax_final_threads=2;
(Expression)
ExpressionTransform2
(SettingQuotaAndLimits)
(Limit)
Limit2→2
(ReadFromMergeTree)
ExpressionTransform2
CollapsingSortedTransform2
Copy1→2
AddingSelector
ExpressionTransform
MergeTree0→1
从CollapsingSortedTransform这一步开始已经是多线程执行,但是读取part部分的动作还是串行。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
一、Java垃圾回收器主要有哪些? 服务器托管网UseSerialGC、UseParallelGC、UseConcMarkSweepGC、UseParalle服务器托管网lNewGC、UseParallelOldGC、UseG1GC 服务器托管,北京服务器托管…