【ClickHouse】通过开心消消乐更新和删除数据|原来是用这种方式解决的吗

本文为稀土掘金技术社区首发签约文章,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的顺序紊乱

这样就会导致数据无法正确消除

VersionedCollapsingMergeTreeCollapsingMergeTree的基础上添加了版本号来保证顺序

如果大家有兴趣可以自行尝试

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());

插入两条数据之后也需要通过finaloptimize的方式来操作

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提供了CollapsingMergeTreeReplacingMergeTree等表引擎通过插入数据间接的实现更新和删除功能

也可以通过TTLPartition来达到删除数据的效果

相关推荐
天冬忘忧9 分钟前
Kafka 生产者全面解析:从基础原理到高级实践
大数据·分布式·kafka
hummhumm17 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
J老熊27 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
青云交32 分钟前
大数据新视界 -- Hive 数据仓库:构建高效数据存储的基石(下)(2/ 30)
大数据·数据仓库·hive·数据安全·数据分区·数据桶·大数据存储
AuroraI'ncoding34 分钟前
时间请求参数、响应
java·后端·spring
zmd-zk42 分钟前
flink学习(2)——wordcount案例
大数据·开发语言·学习·flink
电子手信44 分钟前
知识中台在多语言客户中的应用
大数据·人工智能·自然语言处理·数据挖掘·知识图谱
好奇的菜鸟1 小时前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
Alive~o.01 小时前
Go语言进阶&依赖管理
开发语言·后端·golang
许苑向上1 小时前
Dubbo集成SpringBoot实现远程服务调用
spring boot·后端·dubbo