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

news/2024/5/15 19:06:52/文章来源:https://blog.csdn.net/S1124654/article/details/129294600

简介

在写这篇文章的时候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=MergeTreePARTITION BY toYYYYMM(when)ORDER BY (userid, when)

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

CREATE TABLE price (userid UInt32,price_per_gb Float64) ENGINE=MergeTreePARTITION BY tuple()ORDER BY userid

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

CREATE TABLE user (userid UInt32,name String) ENGINE=MergeTreePARTITION BY tuple()ORDER BY userid

物化视图定义

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

目标表如下。

CREATE TABLE download_daily (day Date,userid UInt32,downloads UInt32,total_gb Float64,total_price Float64)ENGINE = SummingMergeTreePARTITION BY toYYYYMM(day) ORDER BY (userid, day)

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

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

CREATE MATERIALIZED VIEW download_daily_mvTO download_daily ASSELECTday AS day, userid AS userid, count() AS downloads,sum(gb) as total_gb, sum(price) as total_priceFROM (SELECTtoDate(when) AS day,userid AS userid,download.bytes / (1024*1024*1024) AS gb,gb * price.price_per_gb AS priceFROM 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 downloadWITH(SELECT groupArray(userid) FROM user) AS user_idsSELECTnow() + number * 60 AS when,user_ids[(number % length(user_ids)) + 1] AS user_id,rand() % 100000000 AS bytesFROM system.numbersLIMIT 5000

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

SELECT day, downloads, total_gb, total_priceFROM 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=MergeTreePARTITION BY toYYYYMM(when)ORDER BY (when, userid)CREATE MATERIALIZED VIEW download_right_outer_mvTO download_right_outerAS SELECTwhen AS when,user.userid AS userid,user.name AS name,bytes AS bytesFROM 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 = SummingMergeTreePARTITION BY toYYYYMM(day) ORDER BY (userid, day, username)

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

CREATE MATERIALIZED VIEW download_daily_with_name_mvTO download_daily_with_name ASSELECTday AS day, userid AS userid, user.name AS username,count() AS downloads, sum(gb) as total_gb, sum(price) as total_priceFROM (SELECTtoDate(when) AS day,userid AS userid,download.bytes / (1024*1024*1024) AS gb,gb * price.price_per_gb AS priceFROM download LEFT JOIN price ON download.userid = price.userid) AS join1LEFT JOIN user ON join1.userid = user.useridGROUP 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_mvENGINE = 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 bytesFROM download INNER JOIN user ON download.userid = user.useridGROUP 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_mvTO download_right_outerAS SELECTwhen AS when,userid,  user.name AS name,bytes AS bytesFROM 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.luyixian.cn/news_show_76228.aspx

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

相关文章

全面了解 B 端产品设计 — 基础扫盲篇

在今天,互联网的影响力与作用与日俱增,除了我们日常生活领域的改变以外,对于商业领域的渗透也见效颇丰。 越来越多的企业开始使用数字化的解决方案来助力企业发展,包括日常管理、运营、统计等等。或者通过互联网的方式开发出新的业务形态,进行产业升级,如这几年风头正劲的…

ReentrantLock详说

目录 尝试加锁 如果加锁不成功 重点 尝试加锁 最外层lock方法 ReentrantLock.class public void lock() {sync.lock();} 进来发现是个抽象方法 abstract static class Sync extends AbstractQueuedSynchronizer abstract void lock(); 底下有两个实现类,一个…

Zookeeper3.5.7版本——Zookeeper的概述、工作机制、特点、数据结构及应用场景

目录一、Zookeeper的概述二、Zookeeper的工作机制三、Zookeeper的特点四、Zookeeper的数据结构五、Zookeeper的应用场景5.1、统一命名服务5.2、统一配置管理5.3、统一集群管理5.4、服务器动态上下线5.5、软负载均衡一、Zookeeper的概述 Zookeeper 是一个开源的分布式的&#x…

39万字完整版智能矿山项目建设整体解决方案

本资料来源网络,仅做知识分享,请勿商用。完整资料领取见文末,部分资料内容: 1.1 总体技术要求 1.1.1 核心业务架构 智能矿山业务架构是在统一的标准与规范及安全运维保障体系下,按分层设计模式,分为设备层、…

【基于机器学习的推荐系统项目实战-1】初识推荐系统

本文目录一、为什么我们需要推荐系统?二、推荐系统的发展阶段三、推荐系统模型四、通用推荐系统框架4.1 数据生产4.2 数据存储4.3 算法召回4.4 结果排序4.5 结果应用4.6 新浪微博的框架开源结构图五、推荐常用特征5.1 用户特征5.2 物品特征六、推荐常用算法七、结果…

【自动化测试】一位自动化测试工程师居然不会封装框架?神秘自动化测试框架......

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

山寨APP频出?安全工程师和黑灰产在较量

在山寨这个领域,没有人比黑灰产更懂模仿。 据安全从业者介绍,一般而言,对于成熟的山寨开发者来说,几天时间内就可以做出一套前端框架。服务器、源代码、域名、服务商这些内容的创建,通过网上租赁的方式就可以解决。 比…

小红书“复刻”微信,微信“内造”小红书

配图来自Canva可画 随着互联网增长红利逐渐见顶,各大互联网平台对流量的争夺变得愈发激烈。而为了寻找新的业务可能性,各家都在不遗余力地拓宽自身边界。在此背景下,目前最为“吸睛”和“吸金”的社交、电商、种草、短视频等领域&#xff0c…

Qt音视频开发19-vlc内核各种事件通知

一、前言 对于使用第三方的sdk库做开发,除了基本的操作函数接口外,还希望通过事件机制拿到消息通知,比如当前播放进度、音量值变化、静音变化、文件长度、播放结束等,有了这些才是完整的播放功能,在vlc中要拿到各种事…

【vulhub漏洞复现】CVE-2018-2894 Weblogic任意文件上传漏洞

一、漏洞详情影响版本weblogic 10.3.6.0、weblogic 12.1.3.0、weblogic 12.2.1.2、weblogic 12.2.1.3WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应…

快速吃透π型滤波电路-LC-RC滤波器

π型滤波器简介 π型滤波器包括两个电容器和一个电感器,它的输入和输出都呈低阻抗。π型滤波有RC和LC两种, 在输出电流不大的情况下用RC,R的取值不能太大,一般几个至几十欧姆,其优点是成本低。其缺点是电阻要消耗一些…

计算机组成原理4小时速成6:输入输出系统,io设备与cpu的链接方式,控制方式,io设备,io接口,并行串行总线

计算机组成原理4小时速成6:输入输出系统,io设备与cpu的链接方式,控制方式,io设备,io接口,并行串行总线 2022找工作是学历、能力和运气的超强结合体,遇到寒冬,大厂不招人&#xff0c…

操作系统——11.线程概念和多线程模型

这篇文章我们来讲一下操作系统中的线程概念和多线程模型 目录 1.概述 2.线程 2.1问题提出 2.2线程概念 2.3带来的变化 2.4线程的属性 2.5线程的实现方式 2.5.1用户级线程 2.5.2内核级线程 2.5.3相互组合 2.6多线程模型 2.6.1多对一模型 2.6.2一对一模型 2.6.3多…

linux代码库生成-make示例

1、add.c代码实现加法运算: int add(int a,int b) { return ab; } 2、头文件add.h #ifndef ADD_H #define ADD_H int add(int a,int b); #endif 3、CMakeLists.txt编写 cmake_minimum_required(VERSION 2.6) set(CMAKE_C_FLAGS -m32) project(test_add) include_dir…

骨传导耳机推荐,列举几款骨传导主流的耳机分享

随着科技的发展,耳机也发生了巨大的变化,从最初的传统入耳式耳机到骨传导耳机,越来越多的人开始使用这种听音乐的方式。它可以带给我们更好的体验。骨传导耳机顾名思义就是一种类似骨传导发声原理的无线耳机。这篇文章就是介绍目前市面上比较…

【快速理解和上手】逻辑回归logistic regression (无原理,只有公式化步骤)

逻辑回归 Logistic regression 目的:解决二分类问题 数学效果:根据数据(x⃗,y)(\vec{x}, y)(x,y) (其中y为0或1),拟合一条曲线,x轴表示特征,y轴表示一个概率,即这个输入 xxx 对应着…

《OpenGL宝典》--统一变量

统一变量 [layout (location 0)] uniform float f 1.0f;若设置layout,则不需要使用glGetUniformLocation来获取统一变量的位置 使用glUniform*传递值,glUniformMatrix*()设置矩阵统一变量。 glUseProgram(myShader); glUniform1f(0,45.2f);//0为loc…

香港双重牌照、准入安排和禁止事项等重要制度已明确 20多万字的《虚拟资产咨询文件》以证雄心

前不久,香港证监会就加密货币交易发布的《有关适用于获证券及期货事务监察委员会发牌的虚拟资产交易平台营运者的建议监管规定的咨询文件》(以下简称《咨询文件》),并如期就有关监管虚拟资产交易平台的建议展开咨询,以…

PT100铂电阻温度传感器

PT100温度传感器又叫做铂热电阻。     热电阻是中低温区﹡常用的一种温度检测器。它的主要特点是测量精度高,性能稳定。其中铂热电阻的测量精确度是﹡高的,它不仅广泛应用于工业测温,而且被制成标准的基准仪。金属热…

储、AI、车?特斯拉宏图第三篇章即将揭晓

美东时间周三(3月1日)下午4点,特斯拉将在得州总部举办备受瞩目的“投资者日”活动,马斯克之前所提出的“宏图第三篇章(Master Plan Part 3)”的悬念也即将揭晓。马斯克宏图计划到底是什么?关于第三篇我们可以有那些期待…