【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来达到删除数据的效果

相关推荐
专注VB编程开发20年3 分钟前
asp.net mvc如何简化控制器逻辑
后端·asp.net·mvc
点赋科技5 分钟前
沙市区举办资本市场赋能培训会 点赋科技分享智能消费新实践
大数据·人工智能
YSGZJJ25 分钟前
股指期货技术分析与短线操作方法介绍
大数据·人工智能
Doker 多克31 分钟前
Flink CDC —部署模式
大数据·flink
用户67570498850233 分钟前
告别数据库瓶颈!用这个技巧让你的程序跑得飞快!
后端
Guheyunyi35 分钟前
监测预警系统重塑隧道安全新范式
大数据·运维·人工智能·科技·安全
千|寻1 小时前
【画江湖】langchain4j - Java1.8下spring boot集成ollama调用本地大模型之问道系列(第一问)
java·spring boot·后端·langchain
程序员岳焱1 小时前
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
后端·sql·mysql
龚思凯1 小时前
Node.js 模块导入语法变革全解析
后端·node.js