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

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 函数

SELECTuser_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 ASCQuery 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 MergeTreeorder 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 有更高的性能

SELECTuser_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 BYuser_id,date,city,age,sex
ORDER BY user_id ASCQuery 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.mzph.cn/news/718368.shtml

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

相关文章

Spring-静态代理VS动态代理/实现代理ProxyFactory

文章目录 静态代理VS动态代理Spring实现代理ProxyFactory 工作中遇到问题整理动态代理异常com.sun.proxy.$Proxy0 cannot be cast to 静态代理VS动态代理 静态代理VS动态代理 参考URL: https://blog.csdn.net/qq_25881443/article/details/103245938 【java项目实战】代理模式…

【C语言】剖析qsort函数的实现原理

主页:17_Kevin-CSDN博客 专栏:《C语言》 本文将从回调函数,qsort函数的应用,qsort函数的实现原理三个方面进行讲解,请自行跳转至相对位置进行阅读~ 目录 回调函数 qsort函数的应用 qsort函数实现原理 回调函数 什…

mysql主从库Slave_SQL_Running: No问题经验分享

最近在创建mysql主从库的时候,遇到一个问题。执行 mysql> SHOW SLAVE STATUS\G结果显示 Slave_IO_Running: Yes Slave_SQL_Running: No 很是苦恼,查询了很久没有解决 执行 mysql> SELECT * FROM performance_schema.replication_applier_status_…

独立游戏《星尘异变》UE5 C++程序开发日志1——项目与代码管理

写在前面:本日志系列将会向大家介绍在《星尘异变》这款模拟经营游戏,在开发时用到的与C相关的泛用代码与算法,主要记录UE5C与原生C的用法区别,以及遇到的问题和解决办法,因为这是我本人从ACM退役以后第一个从头开始的项…

代码随想录算法训练营第五十天 | 买股票2

目录 买卖股票的最佳时机III买卖股票的最佳时机IV LeetCode 123.买卖股票的最佳时机III LeetCode 123.买卖股票的最佳时机IV 买卖股票的最佳时机III 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。…

牛客周赛 Round 35(A,B,C,D,E,F,G)

这场简单,甚至赛时90分钟不到就AK了。比赛链接,队友题解友链 刚入住学校监狱,很不适应,最近难受的要死,加上最近几场CF打的都不顺利,san值要爆掉了,只能慢慢补题了。 这场C是个滑动窗口&#…

冒泡排序 和 qsort排序

目录 冒泡排序 冒泡排序部分 输出函数部分 主函数部分 总代码 控制台输出显示 总代码解释 冒泡排序优化 冒泡排序 主函数 总代码 代码优化解释 qsort 排序 qsort 的介绍 使用qsort排序整型数据 使用qsort排序结构数据 冒泡排序 首先,我先介绍我的冒泡…

模糊搜索小案例

C#窗体实现数据录入与模糊搜索小案例 记录一下 主要代码 private void button1_Click(object sender, EventArgs e){string name textBox1.Text;string hometown textBox4.Text;string school textBox6.Text;string sex textBox5.Text;string lat textBox3.Text;string …

c#打印BarTend标签提示:具名数据源没有cuckoo*具名数据(解决)

c#打印BarTend标签提示:具名数据源没有cuckoo*具名数据(解决) 今天咕咕更新打印模板的时候遇到的问题,就是在模版中配置了字段名,但是启动c#应用,后端发送json数据打印的时候c#报错提示,没有在…

python 小游戏《2048》字符版非图形界面

参考链接: 闲谈2048小游戏和数组的旋转及翻转和转置 目录 2048 一、方阵类 二、随机插入1或2 三、 合并和递增 四、 判断和移动 五、 键盘控制 完整源代码 玩法过程 2048 上回说到2048小游戏中数组的各种旋转、翻转的方法,就是为代码编程作准…

第十六天-爬虫selenium库

目录 1.介绍 2.使用 selenium 1.安装 2.使用 1.测试打开网页,抓取雷速体育日职乙信息 2.通过xpath查找 3.输入文本框内容 send_keys 4.点击事件 click 5.获取网页源码: 6.获取cookies 7.seleniumt提供元素定位方式:8种 8.控制浏览…

Spring Security OAuth2如何自定义返回的 Token 信息

文章目录 Spring Security OAuth2如何自定义返回的 Token 信息定制不透明令牌的信息Springsecurity-oauth2之TokenEndPoint参考Spring Security OAuth2如何自定义返回的 Token 信息 Spring Boot+OAuth2,如何自定义返回的 Token 信息? 参考URL: https://www.jianshu.com/p/b7…

【Go】指针的声明和初始化

package mainimport "fmt"func main() {// 声明一个整数变量var num int 42// 声明一个指向整数的指针变量,并将其初始化为指向整数变量的地址var ptr *int &num// 打印整数变量的值和指针变量的值(即整数变量的地址)fmt.Pri…

2024第24届中国国际工业博览会新能源与智能网联汽车展电池制造展馆

2024第24届中国国际工业博览会新能源与智能网联汽车展电池制造展馆 时间:2024年9月24日-28日 地点:国家会展中心(上海) 主办单位:工业和信息化部、国家发展和改革委员会、科学技术部、商务部、中国科学院、中国工程…

【游记】GDOI2024

GDOI2024游记 老年退役选手。NOIP 218 分,GDOI 纯纯旅游。 Day -5 周日返校,开始停课。 开始攒 rp。 Day -4 模拟赛,犯困,啥也不会。 下午打球。 Day -3 模拟赛,不困,还是啥也不会。 下午打球。 …

CSS3单独制作移动端页面布局方式(流式布局、flex弹性布局)

目录 1. 流式布局(百分比布局)2. flex弹性布局(强烈推荐)2.1 介绍2.2 Flex容器常见属性2.2.1 flex-direction2.2.2 justify-content2.2.3 flex-wrap2.2.4 align-items2.2.5 align-content2.2.6 flex-flow 2.3 Flex项目常见属性2.3.1 flex2.3.2 align-self和order 1. 流式布局(百…

银河麒麟之Workstation安装

一、VMware Workstation简介 VMware Workstation是一款由VMware公司开发的虚拟化软件,它允许用户在一台物理计算机上运行多个操作系统,并在每个操作系统中运行多个虚拟机。VMware Workstation提供了一个可视化的用户界面,使用户可以轻松创建、…

程序环境和预处理(2)

文章目录 3.2.7 命名约定 3.3 #undef3.4 命令行定义3.5 条件编译3.6 文件包含3.6.1 头文件被包含的方式3.6.2 嵌套文件包含 4. 其他预处理指令 3.2.7 命名约定 一般来讲函数和宏的使用语法很相似,所以语言本身没法帮我们区分二者,那我们平时的一个习惯是…

linux条件判断之if-then

if..then是最常见的条件判断语句,简而言之,就是当符合某个条件判断的时候,就予以进行某项工作。 1.if-then格式 if-then格式1: if [ 条件判断表达式 ];then 当条件判断表达式成立时,需执行的命令 fi if-then格式2…

Redis安全加固策略:绑定Redis监听的IP地址 修改默认端口 禁用或者重命名高危命令

Redis安全加固策略:绑定Redis监听的IP地址 & 修改默认端口 & 禁用或者重命名高危命令 1.1 绑定Redis监听的IP地址1.2 修改默认端口1.3 禁用或者重命名高危命令1.4 附:redis配置文件详解(来源于网络) 💖The Beg…