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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JVM——JVM与Java体系结构

文章目录 1、Java及JVM简介1.1、Java是跨平台的语言1.2、JVM是跨语言的平台 2、Java发展里程碑3、Open JDK和Oracle JDK4、虚拟机与JVM4.1、虚拟机4.2、JVM 5、JVM整体结构6、Java代码执行流程7、JVM的架构模型7.1、基于栈式架构的特点7.2、基于寄存器架构的特点 8、JVM的生命周…

Freesia项目介绍

项目介绍 这是一个Spring Boot Vue的前后端分离项目,实现的是一个通用的后台管理系统。 框架使用 前端使用了layui-vue和layui-vue-admin,分别提供了组件和前端整体架构的支持。 后端使用Spring Boot框架管理 项目技术使用 前端 Layui-vue、Layui…

飞天使-学以致用-devops知识点3-安装jenkins

文章目录 构建带maven环境的jenkins 镜像安装jenkinsjenkins yaml 文件安装插件jenkins 配置k8s创建用户凭证 构建带maven环境的jenkins 镜像 # 构建带 maven 环境的 jenkins 镜像 docker build -t 192.168.113.122:8858/library/jenkins-maven:jdk-11 .# 登录 harbor docker …

Socket网络编程(三)——TCP快速入门

目录 概述TCP连接可靠性1. 三次握手过程2. 四次挥手过程3. 为什么挥手需要四次? 传输可靠性TCP核心APITCP传输初始化配置&建立连接客户端创建Socket建立连接服务端创建ServerSocket监听连接ServerSocket 和 Socket的关系 Socket基本数据类型传输客户端数据传输服…

阿里云A10推理qwen

硬件配置 vCPU:32核 内存:188 GiB 宽带:5 Mbps GPU:NVIDIA A10 24Gcuda 安装 wget https://developer.download.nvidia.com/compute/cuda/12.1.0/local_installers/cuda-repo-rhel7-12-1-local-12.1.0_530.30.02-1.x86_64.rpm s…

php docx,pptx,excel表格上传阿里云,腾讯云存储后截取第一页生成缩略图

php把word转图片的方法:首先给服务器安装libreoffice;然后使用exec函数来调用命令行操作;最后通过“exec(“soffice --headless --invisible…””方法把word转图片即可。 服务器环境:centos7 *集成环境:宝塔 我们开始给服务器安装libreoffice 直接执行下面的代码就可以…

【Kafka系列 06】Kafka Producer源码解析

温馨提示:本文基于 Kafka 2.3.1 版本。 一、Kafka Producer 原理图 生产者的 API 使用还是比较简单,创建一个 ProducerRecord 对象(这个对象包含目标主题和要发送的内容,当然还可以指定键以及分区),然后调…

数据库之ACID

一、ACID **原子性(Atomicity):**即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做,不能只做一部分; 一致性(Consistency):在事务执行前数据…

微服务API网关---APISIX

最近在做微服务调研,看到了apisix这个网关,于是进行了初步了解一下。 微服务是指,将大型应用分解成多个独立的组件,其中每个组件都各自的负责对应项目。 系统的架构大致经历了:单体应用架构–> SOA架构 -->微服务…

DTD、XML阐述、XML的两种文档类型约束和DTD的使用

目录 ​编辑 一、DTD 什么是DTD? 为什么要使用 DTD? 内部 DTD 声明 具有内部 DTD 的 XML 文档 外部 DTD 声明 引用外部 DTD 的 XML 文档 二、XML 什么是XML? XML 不执行任何操作 XML 和 HTML 之间的区别 XML 不使用预定义的标记…

Mallox勒索病毒的最新威胁:如何恢复您的数据?

引言: 在当今数字化时代,网络安全威胁层出不穷,而勒索软件(Ransomware)是其中最为恶劣的一种形式之一。而.Mallox勒索病毒则是近期备受关注的一种勒索软件,其深受全球各地用户的困扰。那么,让我…

postman测试接口

1、postman测试接口 (1)首先安装postman 下载地址:Download Postman | Get Started for Free 选择对应版本下载,然后安装即可 (2)使用postman发送请求 比如以下这个请求例子: 使用postman发…

Qt CMake 国际化相关配置

文章目录 更新ts文件发布ts文件 本来用qmake使用pro文件很简单的一件事,结果用cmake折腾了半天。 何必呢~ 参考:QT6.3 CMake 多语言切换 这是我的 cmake_minimum_required(VERSION 3.16)project(testQml3_6 VERSION 0.1 LANGUAGES CXX)set(CMAKE_AUTO…

mini-spring|关于Bean对象作用域以及FactoryBean的实现和使用

需求 FactoryBean 直接配置FactoryBean 获取FactoryBean中的Bean对象 FactoryBean的getObject方法通过反射获取Bean对象 由此省去对实体Dao类的定义 解决方法 对外提供一个可以二次从 FactoryBean 的 getObject 方法中获取对象的功能即可 整体架构 整个的实现过程包括了两部…

Python matplotlib

目录 1、安装 matplotlib 2、绘制折线图 修改标签文字和线条粗细 校正图形 3、绘制散点图 绘制单点 绘制一系列点 自动计算数据 删除数据点的轮廓 自定义颜色 使用颜色映射 自动保存图表 4、随机漫步 创建 RandomWalk() 类 选择方向 绘制随机漫步图 给点着色 …

Groovy - 大数据共享搜索配置

数据共享搜索列中配置了搜索列,相应的数据共享接口中也需要支持根据配置的字段搜索,配置实体时,支持搜索的入参code必须是searchKeys,且接口应该是需要支持分页(入参必须是 current、pageSize)的。current …

【Excel PDF 系列】iText 库直接实现表格 PDF

你知道的越多,你不知道的越多 点赞再看,养成习惯 如果您有疑问或者见解,欢迎指教: 企鹅:869192208 文章目录 前言生成表格 PDF 效果引入 pom 配置代码实现定义 CreateExcelToPdfModel 对象主方法 前言 最近遇到生成 E…

QEMU之内存虚拟化

内存虚拟化方案 最直观的方案,将QEMU进程的虚拟地址空间的一部分作为虚拟机的物理地址。但该方案有一个问题: 在物理机上,CPU对内存的访问在保护模式下是通过分段分页实现的,在该模式下,CPU访问时使用的是虚拟地址&am…

9 款顶级 iPhone 系统修复软件,可修复各种 iPhone 软件问题

iOS的封闭性和纯粹性仍然无法让iPhone免受潜在风险的影响。iPhone 存在常见问题,包括iPhone/iPad 卡住 Apple 徽标、iOS 更新无法充电问题、iPhone 耳机问题等等。 通常,在这种情况下,您的 iPhone 数据可能无法访问,甚至面临很大…

LeetCode 刷题 [C++] 第148题.排序链表

题目描述 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 题目分析 根据题意,可以使用归并排序来对链表进行排序。归并排序是基于分治的思想,比较容易实现的就是自顶向下的递归方式来实现。 先找出链表的中点&#x…