本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
在上一篇文章【ClickHouse】建表风波中,我们知道ClickHouse
直接执行更新和删除语句的代价是非常大的
所以在这篇文章中我们就用其他的方式来实现更新和删除数据
更新
CollapsingMergeTree
ClickHouse
给我们提供了一种表引擎CollapsingMergeTree
这种表引擎有什么特性呢
CollapsingMergeTree
需要指定一个标记字段sign
(取值为1或-1)
当两条相同数据的标记字段sign
相反(一条为1一条为-1)的时候,这两条数据就会被相互抵消
所以我们可以先插入一条sign
相反(值为-1)的数据把之前的数据消除
然后再插入一条新的数据来达到更新的效果
新建一张表使用CollapsingMergeTree
表引擎
sql
create table collapsing_test
(
id Int64,
name String,
create_time DateTime,
sign Int8
)
engine = CollapsingMergeTree(sign) ORDER BY id;
在引擎中指定我们的sign
字段
接着插入一条数据
sql
insert into collapsing_test(id, name, create_time, sign) VALUES (1, 'name1', now(), 1);
通过查询语句可以看到现在表中有一条数据
id | name | create_time | sign |
---|---|---|---|
1 | name1 | 2024-04-20 09:09:58 | 1 |
那现在要怎么消除这条数据呢
sql
insert into collapsing_test(id, name, create_time, sign) VALUES (1, 'name1', '2024-04-20 09:09:58', -1);
只需要插入一条sign
为-1
的数据就能抵消两条数据了
等下!除了sign
这个字段,其他字段要和原数据一模一样吗
不需要哦,只需要id
一样就行了
因为我们在建表时order by
指定的是id
所以只需要id
相同,sign
相反就能抵消之前的那条数据了
我们的更新操作就可以这样实现
sql
insert into collapsing_test(id, name, create_time, sign) VALUES
(1, '', now(), -1),
(1, 'new_name1', now(), 1);
执行插入两条数据
第一条填充一些默认值来消除之前的数据
第二条插入我们要更新的数据
现在让我们看看两条数据是否抵消了
sql
select * from collapsing_test;
查询结果:
id | name | create_time | sign |
---|---|---|---|
1 | name1 | 2024-04-20 09:09:58 | 1 |
1 | 2024-04-20 09:19:18 | -1 | |
1 | new_name1 | 2024-04-20 09:19:18 | 1 |
两条数据怎么没有抵消呢
因为对数据的抵消是ClickHouse
不定时在后台进行的
所以并不是及时生效的
那怎么才能符合我们的查询要求呢
在查询的时候对数据进行处理
我们可以在查询语句最后加上final
sql
select * from collapsing_test final;
这种方式会在查询的时候对数据进行处理
强制 ClickHouse 进行全量数据处理
sql
optimize table collapsing_test final;
select * from collapsing_test;
这个操作虽然能够获得正确的数据
但是会对全量的数据进行处理
需要控制好频率
通过 group by 自定义 sql
sql
select id, groupArray(name)[1] as name, groupArray(create_time)[1] as create_time
from (select * from collapsing_test where sign > 0 order by create_time desc) group by id;
这种方式其实就是按sign
进行过滤然后按时间倒序
接着根据id
分组并取每组第一个数据
VersionedCollapsingMergeTree
虽然CollapsingMergeTree
可以通过sign
来消除数据
但是sign
是需要按照1和-1这样的顺序才能正确消除的
一般情况下我们不会把-1和1两条数据拼在一起插入
而是使用一些持久层框架先插入-1再插入1
如果有多个地方同时更新数据(或是使用-1删除数据)
就有可能出现-1和1的顺序紊乱
这样就会导致数据无法正确消除
VersionedCollapsingMergeTree
在CollapsingMergeTree
的基础上添加了版本号来保证顺序
如果大家有兴趣可以自行尝试
ReplacingMergeTree
虽然CollapsingMergeTree
可以通过消除和新增来更新数据
但是相对来说还是有点不方便
ReplacingMergeTree
可以直接根据版本号或时间戳保留最新的数据
sql
create table replacing_test
(
id Int64,
name String,
create_time DateTime
)
engine = ReplacingMergeTree(create_time) ORDER BY id;
我们使用create_time
来对数据进行替换
相同id
的情况下,保留create_time
更大的那条数据
sql
insert into replacing_test(id, name, create_time) VALUES (1, 'name1', now());
insert into replacing_test(id, name, create_time) VALUES (1, 'name2', now());
插入两条数据之后也需要通过final
或optimize
的方式来操作
sql
select * from replacing_test final;
optimize table replacing_test final;
select * from replacing_test;
删除
CollapsingMergeTree
CollapsingMergeTree
我们已经知道可以用sign
取值-1来消除对应的数据就能够达到删除的效果
TTL
ClickHouse
支持对列或表设置过期时间
列级TTL
我们只需要在创建表的时候指定列的TTL就可以了
sql
create table ttl_test
(
id Int64,
name String TTL create_time + interval 30 second,
create_time DateTime
)
engine = MergeTree() ORDER BY id;
指定name
字段在创建之后的30秒后过期
插入一条数据
sql
insert into ttl_test(id, name, create_time) VALUES (1, 'name1', now());
等30s之后,我们再查询
id | name | create_time |
---|---|---|
1 | 2024-04-20 19:09:58 |
name
这个字段就被清空了
表级TTL
sql
create table ttl_test
(
id Int64,
name String,
create_time DateTime
)
engine = MergeTree()
ORDER BY id TTL create_time + interval 30 second;
只要把TTL这部分内容移到外面就行了
当我们再次插入一条数据
sql
insert into ttl_test(id, name, create_time) VALUES (1, 'name1', now());
30秒之后查询就会发现这条记录已经被删除了
表级的TTL比较适合有固定存储期限的日志类数据
比如保留最近半年或一年的数据
就可以设定过期时间
等时间到了数据就会被自动删除啦
Partition
ClickHouse
中有一个分区的概念
分区能够帮助ClickHouse
提升查询效率
大家应该都在驿站取过快递
我们取快递的时候都有一个取件码,比如7-3-2289
取件码能够帮助我们快速的找到快递在哪个快递架(7)的哪一层(3)
我们只需要在这一层的快递中遍历一遍找到对应的快递(2289)就行了
分区就是类似的概念
sql
create table partition_test
(
id Int64,
name String,
create_time DateTime
)
engine = MergeTree()
ORDER BY id
PARTITION BY toYYYYMM(create_time);
我们可以在创建表的时候通过partition by
指定分区健
上面的sql
表示根据年月分区
当我们通过类似create_time >= '2024-04-11 00:00:00' and create_time <= '2024-04-21 00:00:00'
这样的条件查询数据时
ClickHouse
就会直接找到202404
这个分区读取数据,提升查询效率
另外我们可以对分区进行卸载
sql
alter table partition_test detach partition '202404';
或删除
sql
alter table partition_test drop partition '202404';
这样我们就能通过卸载或删除分区达到批量删除数据的效果了
总结
由于更新数据和删除数据的成本过大
ClickHouse
提供了CollapsingMergeTree
和ReplacingMergeTree
等表引擎通过插入数据间接的实现更新和删除功能
也可以通过TTL
和Partition
来达到删除数据的效果