clickhouse 随心所欲的聚合模型-AggregatingMergeTree

news2025/4/23 19:55:27

clickhouse 强大的 MergeTree 系列引擎令人信服,其 ReplacingMergeTree、SummingMergeTree 在数据唯一性和汇总场景中表现非凡。但你是否还有保留最小(大)、平均等预聚合需求,甚至在一个模型中既有唯一性语意也有汇总、最小、最大、平均值语意该如何处理。在 doris 中 Aggregate 数据模型可以轻松解决,那么同为头部 AP 数据库的 clickhouse 是否可以随心所欲的定义聚合模型呢?

一、AggregatingMergeTree

1.1 基本使用

AggregatingMergeTree 表引擎作为 MergeTree 系列引擎也是遵循其家族的基本逻辑的,它能够在合并分区的时候按照预先定义的方式聚合数据。与 ReplacingMergeTree、SummingMergeTree 不同的是表引擎已经内置好了聚合方式,用户只能指定字段在分区合并时对字段进行去重或累加,AggregatingMergeTree 则进一步开发底层给用户,用户需要指定在分区合并时采用何种聚合函数,以及针对哪些字段进行计算,下面是该引擎的使用方式(复刻 doris 官方文档的案例)

drop table if exists tbl_agg;
create table if not exists tbl_agg
(
    `user_id` String comment '用户id',
    `date`    datetime comment '数据灌入日期时间',
    `city`    String comment '用户所在城市',
    `age`     Int8 comment '用户年龄',
    `sex`     Int8 comment '用户性别',
    `last_visit_date` AggregateFunction(anyLast,DateTime) comment '用户最后一次访问时间',
    `cost` AggregateFunction(sum, Int256) comment '用户总消费',
    `max_dwell_time` AggregateFunction(max,Int64) comment '用户最大停留时间',
    `min_dwell_time` AggregateFunction(min,Int64) comment '用户最小停留时间'
) engine AggregatingMergeTree()
      order by (user_id, date, city, age, sex);

AggregateFunction 是 clickhouse 提供的特殊数据类型,它能够以二进制的形式存储中间状态结果。其使用方式也十分特殊,在定义的时候需要提供聚合方式以及数据类型。常用的聚合方式整理如下:

  1. count: 计数非空行数
  2. sum: 累加
  3. max: 最大值
  4. min: 最小值
  5. anyLast: 最后一个非空值
  6. uniq: 去重计数

当然 clickhouse 提供的聚合函数很多,详情可以访问: https://clickhouse.com/docs/en/sql-reference/aggregate-functions/reference

因为 AggregateFunction 是二进制存储的中间结果,我们在插入数据时也需要将明文数据转换为 AggregateFunction 可以接受的数据类型,clickhouse 为每个聚合函数都提供了转换为 AggregateFunction 类型的 *State 函数

insert into tbl_agg
select 10000,
       '2017-10-01',
       '北京',
       20,
       0,
       anyLastState(toDateTime('2017-10-01 06:00:00')),
       sumState(toInt256(20)),
       maxState(toInt64(10)),
       minState(toInt64(10));


insert into tbl_agg
select 10000,
       '2017-10-01',
       '北京',
       20,
       0,
       anyLastState(toDateTime('2017-10-01 07:00:00')),
       sumState(toInt256(15)),
       maxState(toInt64(2)),
       minState(toInt64(2));

同理我们在查询是也需要特殊的函数将 AggregateFunction 类型转换为明文(类似序列化与反序列区别),而查询时需要使用 *Merge 函数

SELECT
    user_id,
    anyLastMerge(last_visit_date) AS last_visit_date,
    sumMerge(cost) AS cost,
    maxMerge(max_dwell_time) AS max_dwell_time,
    minMerge(min_dwell_time) AS min_dwell_time
FROM tbl_agg
GROUP BY user_id
ORDER BY user_id ASC

Query id: 30a237df-6018-42fa-a6a9-1d324e21310d

┌─user_id─┬─────last_visit_date─┬─cost─┬─max_dwell_time─┬─min_dwell_time─┐
│ 100002017-10-01 06:00:0035102 │
└─────────┴─────────────────────┴──────┴────────────────┴────────────────┘

1 row in set. Elapsed: 0.005 sec.

看到这里是否觉得这种方式过于繁琐,连正常的数据插入都需要借助 State 函数,那么在升级改造时将寸步难行。好在上面的方式并不是主流的方式,我们可以借助物化视图来屏蔽 State 过程,让数据插入保持原生。

1.2 优化体验

首先我们创建相同结构的普通表作为底表

drop table if exists tbl_agg_basic;
create table if not exists tbl_agg_basic
(
    `user_id`         String comment '用户id',
    `date`            datetime comment '数据灌入日期时间',
    `city`            String comment '用户所在城市',
    `age`             Int8 comment '用户年龄',
    `sex`             Int8 comment '用户性别',
    `last_visit_date` datetime comment '用户最后一次访问时间',
    `cost`            Int256 comment '用户总消费',
    `max_dwell_time`  Int64 comment '用户最大停留时间',
    `min_dwell_time`  Int64 comment '用户最小停留时间'
) engine MergeTree
      order by (user_id, date, city, age, sex);

之后我们将 State 过程写入物化视图中

drop table if exists mv_tbl_agg;
create materialized view if not exists mv_tbl_agg to tbl_agg
as
select user_id,
       date,
       city,
       age,
       sex,
       anyLastState(last_visit_date) as last_visit_date,
       sumState(cost)                as cost,
       maxState(max_dwell_time)      as max_dwell_time,
       minState(min_dwell_time)      as min_dwell_time
from tbl_agg_basic
group by user_id, date, city, age, sex;

对用户来说将明细数据优雅的写入底表中,tbl_agg 对外提供查询功能,用户无需关系数据怎么序列化

flow

下面我们只需要假装什么都不知道向明细数据表插入数据

insert into tbl_agg_basic
values (10000, '2017-10-01', '北京', 20, 0, '2017-10-01 06:00:00', 20, 10, 10),
       (10000, '2017-10-01', '北京', 20, 0, '2017-10-01 07:00:00', 15, 2, 2),
       (10001, '2017-10-01', '北京', 30, 1, '2017-10-01 17:05:45', 2, 22, 22),
       (10002, '2017-10-02', '上海', 20, 1, '2017-10-02 12:59:12', 200, 5, 5),
       (10003, '2017-10-02', '广州', 32, 0, '2017-10-02 11:20:00', 30, 11, 11),
       (10004, '2017-10-01', '深圳', 35, 0, '2017-10-01 10:00:15', 100, 3, 3),
       (10004, '2017-10-03', '深圳', 35, 0, '2017-10-03 10:20:22', 11, 6, 6);

数据会自动同步到 tbl_agg 中,在查询时我们只需要面向 tbl_agg 此时会比直接查询 tbl_agg_basic 有更高的性能

SELECT
    user_id,
    date,
    city,
    age,
    sex,
    anyLastMerge(last_visit_date) AS last_visit_date,
    sumMerge(cost) AS cost,
    maxMerge(max_dwell_time) AS max_dwell_time,
    minMerge(min_dwell_time) AS min_dwell_time
FROM tbl_agg
GROUP BY
    user_id,
    date,
    city,
    age,
    sex
ORDER BY user_id ASC

Query id: 6f7fd017-9378-4f42-8c20-56bd711487d1

┌─user_id─┬────────────────date─┬─city─┬─age─┬─sex─┬─────last_visit_date─┬─cost─┬─max_dwell_time─┬─min_dwell_time─┐
│ 100002017-10-01 00:00:00 │ 北京 │  2002017-10-01 07:00:0035102 │
│ 100012017-10-01 00:00:00 │ 北京 │  3012017-10-01 17:05:4522222 │
│ 100022017-10-02 00:00:00 │ 上海 │  2012017-10-02 12:59:1220055 │
│ 100032017-10-02 00:00:00 │ 广州 │  3202017-10-02 11:20:00301111 │
│ 100042017-10-01 00:00:00 │ 深圳 │  3502017-10-01 10:00:1510033 │
│ 100042017-10-03 00:00:00 │ 深圳 │  3502017-10-03 10:20:221166 │
└─────────┴─────────────────────┴──────┴─────┴─────┴─────────────────────┴──────┴────────────────┴────────────────┘

6 rows in set. Elapsed: 0.008 sec.

还可以插入几条数据来观察 tbl_agg 的结果是否符合我们定义的聚合语意

二、SimpleAggregateFunction

对于上面的案例其实在查询时依然不方便需要调用 Merge 函数,本质因为 AggregateFunction 使用二进制存储。如果数据以明文存储是不是就不需要这么麻烦,clickhouse 针对这类场景提供了 SimpleAggregateFunction

drop table if exists tbl_agg_s;
create table if not exists tbl_agg_s
(
    `user_id` String comment '用户id',
    `date`    datetime comment '数据灌入日期时间',
    `city`    String comment '用户所在城市',
    `age`     Int8 comment '用户年龄',
    `sex`     Int8 comment '用户性别',
    `last_visit_date` SimpleAggregateFunction(anyLast,datetime) comment '用户最后一次访问时间',
    `cost` SimpleAggregateFunction(sum, Int256) comment '用户总消费',
    `max_dwell_time` SimpleAggregateFunction(max,Int64) comment '用户最大停留时间',
    `min_dwell_time` SimpleAggregateFunction(min,Int64) comment '用户最小停留时间'
) engine AggregatingMergeTree()
      order by (user_id, date, city, age, sex);

此时该模型就可以视为完美复刻了 doris 的聚合模型,因为插入和查询将变得原生化

insert into tbl_agg_s
values (10000, '2017-10-01', '北京', 20, 0, '2017-10-01 06:00:00', 20, 10, 10),
       (10000, '2017-10-01', '北京', 20, 0, '2017-10-01 07:00:00', 15, 2, 2),
       (10001, '2017-10-01', '北京', 30, 1, '2017-10-01 17:05:45', 2, 22, 22),
       (10002, '2017-10-02', '上海', 20, 1, '2017-10-02 12:59:12', 200, 5, 5),
       (10003, '2017-10-02', '广州', 32, 0, '2017-10-02 11:20:00', 30, 11, 11),
       (10004, '2017-10-01', '深圳', 35, 0, '2017-10-01 10:00:15', 100, 3, 3),
       (10004, '2017-10-03', '深圳', 35, 0, '2017-10-03 10:20:22', 11, 6, 6);

select * from tbl_agg_s;

从名字也可以看出,相对 AggregateFunction 就不是那么通用即支持的聚合类型相对较少:

  • any
  • anyLast
  • min
  • max
  • sum
  • sumWithOverflow
  • groupBitAnd
  • groupBitOr
  • groupBitXor
  • groupArrayArray
  • groupUniqArrayArray
  • sumMap
  • minMap
  • maxMap

但这些其实已经够用了,同时在上面的聚合场景下 SimpleAggregateFunction 会有更高的性能。

提问: 为什么没有 count

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1477271.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Simulink系列】——Simulink与Matlab接口使用命令行进行仿真

声明:本系列博客参考有关专业书籍,截图均为自己实操,仅供交流学习! 一、Simulink与Matlab接口 1、Matlab工作区变量设置模块参数 Matlab工作区的变量可以作为模块的设置参数 2、Matlab工作区变量作为输入信号 使用From Worksp…

深度学习--神经网络基础

神经网络 人工神经网络( Artificial Neural Network , 简写为 ANN )也简称为神经网络( NN ),是一种模仿生物神经网络结构和 功能的计算模型 。人脑可以看做是一个生物神经网络,由众多的 神经元…

FLStudio20.8编曲制作软件中文版下载及功能全面介绍

一、主要功能 FL Studio 20.8,作为一款深受音乐制作人和作曲家喜爱的软件,具备多种核心功能,满足从创作到完成的整个音乐制作流程。 音频录制与编辑:用户可以轻松录制外部音频,如乐器演奏、人声等,并在软…

【新书推荐】8.4 逻辑运算指令

本节内容:逻辑运算指令。8086 CPU逻辑运算指令包括NOT、AND、OR、XOR,除NOT指令外,均有两个操作数。逻辑运算指令影响状态标志位。 ■否操作指令NOT指令格式:NOT OPRD。将OPRD取反,然后送回OPRD。操作数可以是8位/16位…

UE5 UE4 不同关卡使用Sequence动画

参考自:关于Datasmith导入流程 | 虚幻引擎文档 (unrealengine.com) 关卡中的Sequence动画序列,包含特定关卡中的Actor的引用。 将同一个Sequcen动画资源放入其他关卡,Sequence无法在新关卡中找到相同的Actor,导致报错。 Sequen…

2024国际生物发酵展览会独家解读-力诺天晟科技

参展企业介绍 北京力诺天晟科技有限公司,专业致力于智能仪器仪表制造,工业自动控制系统用传感器、变送器的研发、设计、销售和服务。 公司坐落于首都北京行政副中心-通州区,下设生产子公司位于河北香河经济开发区,厂房面积 300…

Vue NextTick工作原理及使用场景

$nextTick的定义及理解: 定义:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。 所以就衍生出了这个获取更新后的DOM的Vue方法。所以放在Vue.nextTick()回调函数中的执行的应该是会对DOM进行操…

GitHub标星50k的Android全栈技术知识,成为一名合格Android架构师

JNI 简介 JNI (Java Native Interface英文缩写),译为Java本地接口。是Java众多开发技术中的一门技术,意在利用本地代码,为Java程序提供更高效、更灵活的拓展。尽管Java一贯以其良好的跨平台性而著称,但真正…

js 面试运行机制和存储(从以下几方面理解),栈和堆的理解

1 工作原理 每个浏览器都有自己的引擎,通过引擎把代码解析运行起来。 2 生命周期 3-1 内存分配 3-2 内存使用 3-3 内存回收 3 栈和堆的理解 timer也是个函数--所以也是引用类型。 4 如何运行 以下可忽略 首先声明变量,放在左侧栈中执行,在执行…

FPGA-时钟管理单元

时钟管理单元(Clock Management Tile, CMT) : 即时钟管理片,是FPGA器件中一个十分重要的时钟资源。能够对内部和外部的时钟去偏斜、去抖动,同时还支持频率合成、分倍频等功能。 举例,下面这个例子数据从FIFO输出时钟频率为125MHZ,这个时钟频率可以通过…

Redis冲冲冲——Redis的主从复制,哨兵模式以及SpringBoot的配置

目录 引出Redis的主从复制,哨兵模式以及SpringBoot的配置Redis的主从复制Redis的哨兵模式SpringBoot配置 缓存三兄弟:缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出 Redis冲冲冲——Redis的主从复制,哨兵模式以及SpringBoot的配置…

Taro React ---- 在函数组件或类组件中访问上下文(Context)中的值

1. 解决问题的场景 项目是开发好几年了,当时采用的是类组件开发。现在新增需求,新增需求使用的函数组件,组件涉及的层级比较多,如果直接组件传值,比较麻烦。但是单独为这次的需求新增 redux 的引入又觉得没必要。然后在…

优化照片分辨率:如何将照片调整为150dpi,以适应不同场景?

在数字化时代,我们经常需要在不同场景中使用照片,如打印、网页发布、社交媒体分享等。然而,不同场景对于照片的分辨率要求各不相同。有时,我们需要将照片的分辨率调整为特定数值,例如150dpi(每英寸点数)。这样做可以确…

flutter面试题及答案,android面试题最新

前言 今天想停下代码,写点脑袋里不断浮现出来的一些看法。 也就是最近在微博和知乎上老看到“互联网寒冬”的说法。要么是看到啥公司薪水无法如期发放了,要么是看到别人说什么“裁员了,没有交接,签字然后电脑还了就走人&#xf…

【JavaEE进阶】 Spring AOP快速上手

文章目录 🍃什么是AOP🌳什么是Spring AOP🌴上手Spring AOP🚩引入依赖🚩编写AOP程序 ⭕总结 🍃什么是AOP AOP是Aspect Oriented Programming的简称(又称为面向切⾯编程) 什么是面向…

【k8s 访问控制--认证与鉴权】

1、身份认证与权限 前面我们在操作k8s的所有请求都是通过https的方式进行请求,通过REST协议操作我们的k8s接口,所以在k8s中有一套认证和鉴权的资源。 Kubenetes中提供了良好的多租户认证管理机制,如RBAC、ServiceAccount还有各种策路等。通…

练习 1 Web EasySQL极客大挑战

CTF Week 1 EasySQL极客大挑战 BUUCTF 典中典复习 Web SQL 先尝试输入,找一找交互页面 check.php 尝试万能语句 a’ or true SQL注入:#和–的作用 get传参只能是url编码,注意修改编码,输入的字符串要改成url格式。 POST请求和…

定制红酒:定制过程中的沟通与调整,确保满足您的需求

在云仓酒庄洒派的定制红酒服务中,沟通与调整是确保满足消费者需求的关键环节。为了提供上好的服务,云仓酒庄洒派非常重视与消费者的沟通,并根据他们的反馈进行调整,以确保产品符合他们的期望。 首先,在定制过程中&…

Fastjson2 <== 2.0.26反序列漏洞

根据Y4TACKER师傅在2023-03-20发布了一篇关于Fastjson原生反序列化的文章,文章中引入注目的是利用条件限制条件,不常常关注漏洞预警或者内容的几乎都是未发觉Fastjson2 到Fastjson2 2.0.26版本都有问题,其实如果单独去使用一些关键词去搜索&a…

linux系统---nginx基础

目录 一、Nginx的概念 二、Nginx常用功能 1、HTTP(正向)代理,反向代理 1.1正向代理 1.2 反向代理 2、负载均衡 2.1 轮询法(默认方法) 2.2 weight权重模式(加权轮询) 2.3 ip_hash 3、web缓存 三、基础特性 四…