继续上次关于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 '测试表';
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还是很强大的,就是需要理解它的文档说明,姿势对了,他就很香,欢迎持续关注"安前码后",点击下方名片页,更多工作中实用干货会持续输出中。
觉得有帮助的话,帮忙意见三连,感激涕零。
加油,铁子们!!!