ClickHouse进阶篇-多表连接物化视图

news2024/11/14 5:51:02

简介

在写这篇文章的时候doris 1.2 的物化视图只是支持单表建立物化视图,现在说下ClickHouse多表的物化视图。

前言

本文翻译自 Altinity 针对 ClickHouse 的系列技术文章。面向联机分析处理(OLAP)的开源分析引擎 ClickHouse,因其优良的查询性能,PB 级的数据规模,简单的架构,被国内外公司广泛采用。

阿里云 EMR-OLAP 团队,基于开源 ClickHouse 进行了系列优化,提供了开源 OLAP 分析引擎 ClickHouse 的云上托管服务。EMR ClickHouse 完全兼容开源版本的产品特性,同时提供集群快速部署、集群管理、扩容、缩容和监控告警等云上产品功能,并且在开源的基础上优化了 ClickHouse 的读写性能,提升了 ClickHouse 与 EMR 其他组件快速集成的能力。访问https://help.aliyun.com/document_detail/212195.html
了解详情。

在 ClickHouse 物化视图中使用 Join

ClickHouse 物化视图提供了一种在 ClickHouse 中重组数据的强大方法。我们已经在网络研讨会、博客文章和会议讲座中多次讨论了其能力。我们收到的最常见的后续问题之一是:物化视图是否支持 Join。

答案是肯定的。这篇博客文章展示了具体方法。如果你想要简短的答案,那就是:物化视图会触发 Join 中最左侧的表。物化视图将从 Join 中的右侧表提取值,但如果这些表发生变化,则不会触发。

请继续阅读关于物化视图与 Join 行为的详细示例。我们还将解释底层的原理,帮助你在创建自己的视图时更好地理解 ClickHouse 行为。注:示例来自 ClickHouse 版本 20.3。

表定义

物化视图可以用各种有趣的方式转换数据,但我们只说简单的。我们将以 download 表为例,演示如何构建从几个维度表中提取信息的每日下载总数指标。该模式的摘要如下。

我们首先定义 download 表。这个表可能会变得非常大。

CREATE TABLE download (

  when DateTime,

  userid UInt32,

  bytes UInt64

) ENGINE=MergeTree

PARTITION BY toYYYYMM(when)

ORDER BY (userid, when)

接下来,我们定义一个维度表,该表将用户 ID 映射到每 GB 下载量的价格。这个表相对较小。

CREATE TABLE price (

  userid UInt32,

  price_per_gb Float64

) ENGINE=MergeTree

PARTITION BY tuple()

ORDER BY userid

最后,我们定义一个维度表,该表将用户 ID 映射到名称。这个表也同样很小。

CREATE TABLE user (

  userid UInt32,

  name String

) ENGINE=MergeTree

PARTITION BY tuple()

ORDER BY userid

物化视图定义

现在,让我们创建一个物化视图,该视图按用户 ID 汇总每日下载次数和字节数,并根据下载的字节数计算价格。我们需要直接创建目标表,然后使用一个带有 TO 关键字(指向我们的表)的物化视图定义。

目标表如下。

CREATE TABLE download_daily (

  day Date,

  userid UInt32,

  downloads UInt32,

  total_gb Float64,

  total_price Float64

)

ENGINE = SummingMergeTree

PARTITION BY toYYYYMM(day) ORDER BY (userid, day)

上面的定义利用了专门的 SummingMergeTree 行为。任何非键数字字段均视为一个聚合,因此我们不必在列定义中使用聚合函数。

最后,这是我们的物化视图定义。也可以用更紧凑的方式来定义它,但是你很快就会看到,这种形式更容易扩展视图,从而与更多的表 Join。

CREATE MATERIALIZED VIEW download_daily_mv

TO download_daily AS

SELECT

  day AS day, userid AS userid, count() AS downloads,

  sum(gb) as total_gb, sum(price) as total_price

FROM (

  SELECT

    toDate(when) AS day,

    userid AS userid,

    download.bytes / (1024*1024*1024) AS gb,

    gb * price.price_per_gb AS price

  FROM download LEFT JOIN price ON download.userid = price.userid

)

GROUP BY userid, day

加载数据

我们现在可以通过加载数据来测试视图。我们首先加载带有用户名和价格信息的两个维度表。

INSERT INTO price VALUES (25, 0.10), (26, 0.05), (27, 0.01);

INSERT INTO user VALUES (25, 'Bob'), (26, 'Sue'), (27, 'Sam');

接下来,我们将示例样本数据添加到 download 事实表中。下面的 INSERT 添加了 5000 行,按 user 表中列出的 userid 值均匀分布。

INSERT INTO download

  WITH

    (SELECT groupArray(userid) FROM user) AS user_ids

  SELECT

    now() + number * 60 AS when,

    user_ids[(number % length(user_ids)) + 1] AS user_id,

    rand() % 100000000 AS bytes

  FROM system.numbers

  LIMIT 5000

此时我们可以看到,物化视图将数据填充到 download_daily 中。下面是一个示例查询。

SELECT day, downloads, total_gb, total_price

FROM download_daily WHERE userid = 25

┌────────day─┬─downloads─┬───────────total_gb─┬────────total_price─┐

│ 2020-07-14 │       108 │  5.054316438734531 │ 0.5054316438734532 │

│ 2020-07-15 │       480 │  22.81532768998295 │  2.281532768998296 │

│ 2020-07-16 │       480 │  21.07045224122703 │  2.107045224122702 │

│ 2020-07-17 │       480 │ 21.606687822379172 │ 2.1606687822379183 │

│ 2020-07-18 │       119 │  5.548438269644976 │ 0.5548438269644972 │

└────────────┴───────────┴────────────────────┴────────────────────┘

目前还不错。但我们还能更进一步。我们首先看看 ClickHouse 背后的原理。

刨根问底

要有效地使用物化视图,了解其背后的原理是大有帮助的。物化视图作为后插入触发器对单个表运行。如果物化视图定义中的查询包括 Join,那么来源表就是 Join 中的左侧表。

在我们的示例中,download 是左侧表。因此,任何对 download 的插入都会导致一个分片被写入 download_daily。尽管将值添加到了 Join 中,但对 user 的插入没有效果。

如果我们创建一个更有趣的物化视图,就很容易展示这种行为。让我们定义一个对 user 表进行右侧外部联接的视图。在这种情况下,我们将使用一个简单的 MergeTree 表,这样我们就可以看到所有生成的行,而不用像 SummingMergeTree 那样进行合并。下面是一个简单的目标表,后面是一个物化视图,它将从 download 表填充目标表。

CREATE TABLE download_right_outer (

  when DateTime,

  userid UInt32,

  name String,

  bytes UInt64

) ENGINE=MergeTree

PARTITION BY toYYYYMM(when)

ORDER BY (when, userid)

CREATE MATERIALIZED VIEW download_right_outer_mv

TO download_right_outer

AS SELECT

  when AS when,

  user.userid AS userid,

  user.name AS name,

  bytes AS bytes

FROM download RIGHT OUTER JOIN user ON (download.userid = user.userid)

当我们在 download 表中插入一行时会发生什么?物化视图为 user 表中的每次插入*以及*任何不匹配的行生成一行,因为我们进行的是右侧外部联接。(你可能已经注意到了,这个视图也有一个潜在缺陷。我们很快就会处理这个问题。)

INSERT INTO download VALUES (now(), 26, 555)

SELECT * FROM download_right_outer

┌────────────────when─┬─userid─┬─name─┬─bytes─┐

│ 2020-07-12 17:27:35 │     26 │ Sue  │   555 │

└─────────────────────┴────────┴──────┴───────┘

┌────────────────when─┬─userid─┬─name─┬─bytes─┐

│ 0000-00-00 00:00:00 │     25 │ Bob  │     0 │

│ 0000-00-00 00:00:00 │     27 │ Sam  │     0 │

└─────────────────────┴────────┴──────┴───────┘

另一方面,如果你在 user 表中插入一行,物化视图中不会发生任何变化。

INSERT INTO user VALUES (28, 'Kate')

SELECT * FROM download_right_outer

┌────────────────when─┬─userid─┬─name─┬─bytes─┐

│ 2020-07-12 17:27:35 │     26 │ Sue  │   555 │

└─────────────────────┴────────┴──────┴───────┘

┌────────────────when─┬─userid─┬─name─┬─bytes─┐

│ 0000-00-00 00:00:00 │     25 │ Bob  │     0 │

│ 0000-00-00 00:00:00 │     27 │ Sam  │     0 │

└─────────────────────┴────────┴──────┴───────┘

只有当你向 download 表添加更多的行时,才会看到新用户行的效果。

对多个表 Join

像 SELECT 语句一样,物化视图可以对多个表 Join。在第一个示例中,我们 Join 了下载价格(因 userid 而异)。现在我们来 Join 第二个 user 表,该表将 userid 映射到一个 username。在这个示例中,我们将添加一个新的目标表,其中添加了 username 列。由于 username 不是聚合,我们也将其添加到 ORDER BY。这将防止 SummingMergeTree 引擎尝试聚合它。

CREATE TABLE download_daily_with_name (

  day Date,

  userid UInt32,

  username String,

  downloads UInt32,

  total_gb Float64,

  total_price Float64

)

ENGINE = SummingMergeTree

PARTITION BY toYYYYMM(day) ORDER BY (userid, day, username)

现在我们来定义物化视图,它以简单直接的方式扩展了第一个示例的 SELECT。

CREATE MATERIALIZED VIEW download_daily_with_name_mv

TO download_daily_with_name AS

SELECT

  day AS day, userid AS userid, user.name AS username,

  count() AS downloads, sum(gb) as total_gb, sum(price) as total_price

FROM (

  SELECT

    toDate(when) AS day,

    userid AS userid,

    download.bytes / (1024*1024*1024) AS gb,

    gb * price.price_per_gb AS price

  FROM download LEFT JOIN price ON download.userid = price.userid

) AS join1

LEFT JOIN user ON join1.userid = user.userid

GROUP BY userid, day, username

 你可以截断 download 表并重新加载数据,以此测试新视图。这将留给读者作为练习。

慎重许愿

ClickHouse SELECT 语句支持广泛的 Join 类型,这为物化视图所实现的转换提供了很大的灵活性。灵活性可能是把双刃剑,因为它创造了更多的机会,有可能产生并非预期的结果。

例如,如果你在 download 中插入一条 userid 30 的行,会发生什么?这个 userid 在 user 表或 price 表中都不存在。

INSERT INTO download VALUES (now(), 30, 222)

简而言之:如果你不仔细定义物化视图,该行可能不会出现在目标表中。为了确保匹配,你必须进行 LEFT OUTER JOIN 或者 FULL OUTER JOIN。这是有道理的,因为这和运行 SELECT 本身产生的行为是一样的。download_right_outer_mv 示例正是存在如上所述的问题。

视图定义也会产生不易察觉的语法错误。例如,遗漏 GROUP BY 项会导致令人费解的失败。下面是一个简单示例。

CREATE MATERIALIZED VIEW download_daily_join_old_style_mv

ENGINE = SummingMergeTree PARTITION BY toYYYYMM(day)

ORDER BY (userid, day) POPULATE AS SELECT 

    toDate(when) AS day, 

    download.userid AS userid, 

    user.username AS name, 

    count() AS downloads, 

    sum(bytes) AS bytes

FROM download INNER JOIN user ON download.userid = user.userid

GROUP BY userid, day  -- Column `username` is missing!

Received exception from server (version 20.3.8):

Code: 10.DB::Exception: Received from localhost:9000.DB::Exception: Not found column name in block. There are only columns: userid, toStartOfDay(when), count(), sum(bytes).

哪儿出问题了?username 列中遗漏了 GROUP BY。ClickHouse 拒绝视图定义的做法是合理的,但报错信息有点难以解读。

最后,当列在联接的表之间重叠时,务必要仔细指定列。下面是与上文的 RIGHT OUTER JOIN 示例略有不同的版本。

CREATE MATERIALIZED VIEW download_right_outer_mv

TO download_right_outer

AS SELECT

  when AS when,

  userid,  

  user.name AS name,

  bytes AS bytes

FROM download RIGHT OUTER JOIN user ON (download.userid = user.userid)

当你在 download 中插入行时,你会得到如下的结果,其中 userid 已从不匹配的行中删除。

SELECT * FROM download_right_outer

┌────────────────when─┬─userid─┬─name─┬─bytes─┐

│ 0000-00-00 00:00:00 │      0 │ Sue  │     0 │

│ 0000-00-00 00:00:00 │      0 │ Sam  │     0 │

└─────────────────────┴────────┴──────┴───────┘

┌────────────────when─┬─userid─┬─name─┬─bytes─┐

│ 2020-07-12 18:04:56 │     25 │ Bob  │   222 │

└─────────────────────┴────────┴──────┴───────┘

在这种情况下,ClickHouse 似乎输入了默认值,而不是从 user.userid 分配值。你必须明确地命名列值并且使用 AS userid 来分配名称。如果你单独运行 SELECT 查询,是达不到这种效果的。这种行为看起来像一个缺陷。

结论

物化视图是 ClickHouse 用户可用的最通用的功能之一。物化视图是由一个 SELECT 语句填充的,该 SELECT 可以 Join 多个表。要了解的关键是,ClickHouse 仅触发 Join 中最左侧的表。其他表可提供用于转换的数据,但是视图不会对这些表上的插入做出反应。

Join 带来了新的灵活性,但也可能导致意料之外的结果。因此,最好仔细测试物化视图,尤其是存在 Join 时。

参考

Using Joins in ClickHouse Materialized Views – Altinity | The Real Time Data Company

 

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

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

相关文章

蓝库云|ERP系统在企业数字化转型中最常用的八大功能

ERP系统和与企业数字化转型 随着数字化发展的兴起,规划和管理已成为企业产生富有成效的成果的关键。许多企业采用了企业资源规划 (ERP) 等先进工具,使企业所有者能够以高效的方式规划和管理其资源和运营。 ERP系统负责整合业务的不同流程并向决策者提供…

【LeetCode】剑指 Offer 20. 表示数值的字符串 p127 -- Java Version

题目链接:https://leetcode.cn/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/ 1. 题目介绍(20. 表示数值的字符串) 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。 数值(按顺序&#xff…

【软件测试】还在背锅?接口自动化测试5个测试点(重要)8年测试的总结......

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 功能测试 接口的功能…

什么是数据可视化,数据可视化有什么价值

“数据可视化”这个词看上去似乎让人有点摸不着头脑,但其实在实际生活中也有类似的应用。举个例子,媒婆给你介绍对象,前面给你铺垫一堆,如五官端正、身材修长、皮肤雪白(各种繁杂的信息描述),你…

Java集合(一)

目录 Java集合框架概述 Collection接口 Collection接口方法 Iterator迭代器接口 使用 Iterator 接口遍历集合元素 Iterator接口的方法 Iterator接口remove()方法 使用 foreach 循环遍历集合元素 Java集合框架概述 一方面, 面向对象语言对事物的体现都是以对…

一文搞懂华为的HCIA-Datacom(学习视频+模拟器+考试题库)

一、华为认证是什么? 二、为什么要选择华为认证 三、HCIA-Datacom的介绍 1.通过认证验证的能力 中小型园区网络的规划设计、部署实施、运维和优化能力 2.建议掌握的知识 路由交换原理、WLAN基本原理、网络安全基础知识、网络管理与运维基础知识、以及SDN与编程自动…

全球十大正规贵金属交易价格走势app软件排名(2023口碑榜)

最近,随着人们对贵金属产品的需求增加,贵金属价格走势变得更加复杂。为了更好地掌握贵金属市场的变化,很多投资者都在寻找一款可以追踪贵金属价格走势的app软件。 针对这一需求,现在已经有多款贵金属价格走势软件涌现在市场上。…

SSRF学习 3

目录 <1> 什么是SSRF&#xff1f; <2> 通常SSRF会发生在哪些位置&#xff1f; <3> 测试流程 <4> Weblogic-ssrf 复现 (1) 漏洞存在点 (2) 注入HTTP头&#xff0c;利用Redis反弹shell (3) 修复方案 <1> 什么是SSRF&#xff1f; SSRF(Serv…

CentOS安装配置Nginx

一、下载Nginx&#xff1a; 说明&#xff1a; 1&#xff09;因为这是一个stable稳定版本。 2&#xff09;Mainline version: 开发版&#xff1b;Stable version: 稳定版&#xff1b;Legacy versions: 早期版本&#xff1b; 二、上传到Linux中&#xff1a; 三、解压文件&#…

HBase集群部署

目录 一、前期准备 二、HBase下载 1. 查看HBase与hadoop版本对应关系 2. hbase的下载 3. 将hbase的tar包上传到linux 下 二、安装hbase 1. 解压 2. HBase的文件配置 主机名hadoop版本HBase版本hadoop安装路径Hbase安装路径HadoopMaster3.3.02.4.3/home/hadoop/softwareh…

【SSM】篇二:注解开发与AOP

文章目录1、注解开发1.1 注解开发定义bean1.2 纯注解开发模式1.3 注解开发Bean的作用范围和生命周期1.4 注解开发依赖注入1.5 注解开发管理第三方Bean2、Spring整合案例2.1 Spring整合mybatis2.2 Spring整合Junit3、AOP3.1 AOP思想入门案例3.2 AOP工作流程3.3 AOP切入点表达式3…

pg逻辑复制详解

目录 什么是逻辑复制 逻辑解析 复制槽 output plugin ​​​​​​​几个常见的outputplugin 几个能手动接收解析数据的函数和工具 逻辑解析测试1&#xff1a;观察用2个不同的output plugin解析数据 逻辑解析测试2&#xff1a;使用pg_recvlogical工具接收逻辑解析数据&am…

服务器配置 | 在Windows本地显示远程服务器绘图程序

文章目录方法1&#xff1a;在MobaXterm的终端输入指令方法2&#xff1a;在Pycharm中运行前提概要&#xff0c;需要在本地Windows端显示点云的3d可视化界面 对于点云的3d可视化一般有两种方法&#xff0c;open3d显示或者是mayavi显示。这两个库都可以使用pip install来实现安装…

给正在准备PMP考试的同学一些建议

去年年尾参与了pmp考试&#xff0c;顺利通过&#xff0c;并得了5A&#xff0c;让我小小激动了一下。在出来结果之前&#xff0c;我也是坚信自己能过&#xff0c;得知是5A&#xff0c;当然也是非常高兴。话不多说&#xff0c; 先贴图&#xff1a; 这里我将要说一下我都是如何备…

Modbus网关在锂电池干燥箱的应用

在锂离子电池生产过程中&#xff0c;将正负极片辊压绕卷再放入电池盒之后&#xff0c;须对锂电池电芯极组进行烘烤干燥。相信大家也了解水分对锂电池的性能影响是很大的&#xff0c;需要注液前在装配车间将锂离子电池电芯内部的水分去除&#xff0c;以免影响锂电池质量。在锂电…

星光2开发板使用ECR6600U无线wifi网卡的方法

visionfive2 开发板性能还是不错的&#xff0c;有些人买的时候会带一个无线wifi网卡&#xff0c;但是官方提供的操作系统没有驱动。 所以需要自己编驱动&#xff08;他大爷的&#xff09;。 还好有人已经踩过坑了。 星光2之USB无线网卡使用教程【新增RTL8832AU WiFi6双频无线…

开发手册——一、编程规约_3.代码格式

这篇文章主要梳理了在java的实际开发过程中的编程规范问题。本篇文章主要借鉴于《阿里巴巴java开发手册终极版》 下面我们一起来看一下吧。 1. 【强制】大括号的使用约定。如果是大括号内为空&#xff0c;则简洁地写成{}即可&#xff0c;不需要换行&#xff1b;如果是非空代码…

BI的能力边界:能解决的企业问题和不擅长的领域

数字化转型本就需要借助信息化相关技术、思想来完成&#xff0c;所以说信息化建设同样是数字化转型过程中非常重要的一环&#xff0c;而这就是商业智能BI和数字化转型的关系 BI 能解决的企业问题 数据是企业的重要资产&#xff0c;也是企业商业智能BI的核心要求。通常&#x…

【微信小程序】-- 全局配置 -- tabBar(十七)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

【数据可视化工具】浅谈 DataEase 和 FineBI 支持的数据源

前言最近对市面上比较火热的数据可视化工具 DataEase 和 FineBI 进行了调研&#xff0c;在支持的数据源方面感觉不太一样&#xff0c;所以就有了这篇文章&#xff0c;话不多说&#xff0c;我们一起来看一下吧&#xff01;以下的内容&#xff0c;大多来自两个工具的官方文档&…