Clickhouse表引擎之CollapsingMergeTree引擎的原理与使用

前言

继续上次关于clickhouse的一些踩坑点,今天讲讲另外一个表引擎——CollapsingMergeTree。这个对于引擎对于数据量较大的场景是个不错的选择。注意,选择clickhouse的一般原因都是为了高效率查询,提高用户体验感,说白了就是以空间换时间,clickhouse的一个关键设计就是数据的合并。

CollapsingMergeTree官方文档说明

该引擎继承于 MergeTree,并在数据块合并算法中添加了折叠行的逻辑。

CollapsingMergeTree 会异步的删除(折叠)这些除了特定列 Sign 有 1 和 -1 的值以外,其余所有字段的值都相等的成对的行。没有成对的行会被保留。

注意:折叠其实就是跟合并概念类似的意思,这个时间是不一定的,有可能马上合并,有可能过一阵子合并,根本原因是因为合并数据涉及频繁的磁盘IO和空间占用。我猜测它合并一般选择数据库使用频率较低的时候合并。

这个说明什么意思呢?

如果仔细阅读,应该可以理解。就是说,创建表的时候,需要指定一个字段,比如官方的字段用sign表示,用来存一个标志位,标志位只能为-1和1。这样后面如果存在两条记录,除了标志位不一样以外,其他字段完全相同,那么在后续后台数据合并的时候,这两条数据会被相互抵消,彻底地被物理删除。

实操讲解

创建一张表
CREATE TABLE IF NOT EXISTS test (
		`advertiser_account_group_id` Int64 COMMENT '项目ID',
		`landing_page_id` Int64 COMMENT '落地页ID',
		`statistic_date` DateTime COMMENT '统计日期',
		`landing_page_channel_id` Int64 COMMENT '渠道ID',
		`page_view_num` Int64 DEFAULT 0 COMMENT '浏览数',
		`form_submit_num` Int64 DEFAULT 0 COMMENT '表单提交数',
		`sign` INT8 COMMENT '标志位' 
	) ENGINE = CollapsingMergeTree ( sign ) 
PARTITION BY toYYYYMM ( statistic_date ) 
PRIMARY KEY ( advertiser_account_group_id, landing_page_id, landing_page_channel_id, statistic_date ) 
ORDER BY( advertiser_account_group_id, landing_page_id, landing_page_channel_id, statistic_date ) 
COMMENT '测试表';
插入一条sign = 1的数据
INSERT INTO test3 ( advertiser_account_group_id, landing_page_id, landing_page_channel_id, statistic_date, page_view_num, form_submit_num, sign )VALUES(1,2,3,'2024-01-14 11:00:00',10,20,1)

这时候表里只有这一条标志位为1的数据,并且它不存在一条标志位为-1的数据,他就会一直存在于数据库中。假设这个数据我洗错了,需要对他进行修正,因为我这次的业务,涉及到清理的历史数据量级是数十亿的广告pv数据,所以不能直接对表记录进行更新,这个是clickhouse数据库的一个瓶颈。那怎么办呢,我这里是按照天维度进行数据清洗,所以在重新插入修正后的数据之前,先要查询出来我这个时间段内的历史数据,把查询出来的数据,将sign置为-1,再重新插一份到数据库,利用CollapsingMergeTree表隐情的折叠机制自动进行删除。这个过程,查询历史数据的步骤非常关键,按照官方文档里面,涉及数值的字段,需要用sum函数进行查询,SQL如下:

SELECT
	advertiser_account_group_id,
	landing_page_id,
	landing_page_channel_id,
	statistic_date,
	sum( page_view_num * sign ) AS page_view_num,
	sum( form_submit_num * sign ) AS form_submit_num 
FROM
	test3 
	where statistic_date BETWEEN '2024-01-14 00:00:00' and '2024-01-14 23:59:59'
GROUP BY
	advertiser_account_group_id,
	landing_page_id,
	landing_page_channel_id,
	statistic_date 
HAVING
	sum( sign ) > 0

这里一定要这样写,不能直接用如下的SQL写,否则你查询数来的数据大概率是错的:

    SELECT
	advertiser_account_group_id,
	landing_page_id,
	landing_page_channel_id,
	statistic_date,
    sum(page_view_num),
    sum(form_submit_num)
FROM
	test3
	where statistic_date BETWEEN '2024-01-14 00:00:00' and '2024-01-14 23:59:59' and sign > 0
GROUP BY
	advertiser_account_group_id,
	landing_page_id,
	landing_page_channel_id,
	statistic_date

假设我在原有只有一条数据的基础上,再插一条标志位为-1的数据,其他字段一模一样

INSERT INTO test3 ( advertiser_account_group_id, landing_page_id, landing_page_channel_id, statistic_date, page_view_num, form_submit_num, sign )VALUES(1,2,3,'2024-01-14 11:00:00',10,20,-1)

如果直接使用第二个SQL查询,那么查询出来的结果就是第一条数据,如果数据还没有进行合并,我们查出来之后,再次将这一条数据sign = 1的数据设置成sign = -1,再插入数据库,这时候,数据效果就是这样

会存在两条sign=-1的数据,如果后面反复执行这个清洗任务,你插入的数据除了标志位不一样,其他都是一样的情况下,你会发现,你的数据怎么莫名其妙消失了,因为可能会出现你的sign = 1的数据跟数据库里面sign = -1的数据折叠抵消了。因为它合并的时间是不一定的,有可能马上合并,有可能几天之后才合并,所以如果你查询的姿势不对,你的数据就会一直错下去。

所以,正确的查询姿势,必须按照文档说明的取查询,案例可以直接查看官网的Demo或者我的这个也可以。

写在最后

好了,今天的内容就分享到这里,这篇文章有需要的可以好好收藏理解一下,在使用clickhouse的场景中,是个非常不错的选择。它的设计很巧妙,clickhouse还是很强大的,就是需要理解它的文档说明,姿势对了,他就很香,欢迎持续关注"安前码后",点击下方名片页,更多工作中实用干货会持续输出中。

觉得有帮助的话,帮忙意见三连,感激涕零。
加油,铁子们!!!

你可能感兴趣的:(clickhouse,Java,clickhouse,数据库)