当前位置: 首页 > ops >正文

ClickHouse 高性能实时分析数据库-物化视图篇

告别等待,秒级响应!这不只是教程,这是你驾驭PB级数据的超能力!我的ClickHouse视频课,凝练十年实战精华,从入门到精通,从单机到集群。点开它,让数据处理速度快到飞起,让你的职业生涯从此开挂!

全套视频教程联系博主

1 写在前面

clickhouse的物化视图你用对了吗???

在数据仓库领域,我们经常会遇到这样的场景:

  1. 原始数据量巨大:日志、事件流等数据以极高的速度写入。

  2. 查询模式固定:分析师或仪表盘(Dashboard)总是对这些原始数据进行固定的聚合查询,例如:

    1. 每分钟的网站访问量 (PV/UV)

    2. 每个商品的日销售额

    3. 每个接口的平均响应时间

如果每次查询都直接扫描原始数据表,即使 ClickHouse 性能卓越,当数据量达到千亿甚至万亿级别时,查询延迟也会增加,计算资源消耗巨大。

普通视图 (View) 能解决问题吗? 不能。普通视图只是一个查询别名,它不存储任何数据。每次查询视图时,实际上还是在执行视图定义中的那个复杂查询,扫描原始表。

物化视图 (Materialized View) 的诞生 为了解决这个问题,物化视图应运而生。

核心思想:物化视图是一种预计算持久化存储的机制。它像一个“数据触发器”,当其监控的源表有新数据写入时,它会自动对这些新数据执行一个 SELECT 查询,并将结果**物化(存储)**到一个独立的目标表中。后续的查询可以直接访问这个小得多的、预聚合过的目标表,从而实现毫秒级的查询响应。

在 ClickHouse 中,物化视图的概念与其他数据库(如 Oracle, PostgreSQL)有本质区别。理解这一点至关重要。

ClickHouse 物化视图 = 触发器 (Trigger)

你可以把 ClickHouse 的物化视图理解为一个插入触发器。它本身不存储数据,它只是一个数据转换和搬运的规则

  1. 数据写入:当有 INSERT 操作将一批数据写入源表 (Source Table) 时。

  2. 触发执行:该 INSERT 操作会自动触发所有监听此源表的物化视图。

  3. 数据转换:物化视图执行其 AS SELECT ... 子句中定义的查询,但只针对刚刚插入的新数据块进行计算。

  4. 写入目标表:物化视图将转换后的结果集,以 INSERT 的方式写入到预先定义好的目标表 (Target Table) 中。

  5. 查询加速:用户查询时,直接查询这个已经预聚合过的、数据量小得多的目标表,而不是庞大的源表。

重要区别

  • 物化视图本身 (my_mv): 是一个没有数据的逻辑定义/触发器SELECT * FROM my_mv 通常是无意义的。

  • 目标表 (target_table): 是一个真实的、存储了预计算结果的物理表。我们最终查询的是它。

2 使用示例

场景:实时统计网站每分钟的页面浏览量(PV)和独立访客数(UV)。 

-- 存储原始访问日志
CREATE TABLE visits_raw
(`timestamp` DateTime,`url` String,`user_id` String
)
ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (timestamp, url);INSERT INTO visits_raw (timestamp, url, user_id) VALUES
(now(), '/page/1', 'user-a'),
(now(), '/page/2', 'user-b'),
(now(), '/page/1', 'user-a'),
(now() + 2, '/page/3', 'user-c');

目标表用于存储预聚合的结果。这里的表引擎选择至关重要。对于聚合场景,AggregatingMergeTree 是最佳选择。

AggregatingMergeTree 引擎: 它专门用于增量聚合。它会将 SELECT 查询中带有 -State 后缀的聚合函数(如 countState, uniqState)的中间状态存储起来。在查询时,再使用 -Merge 后缀的函数(如 countMerge, uniqMerge)来完成最终计算。这使得后台合并和最终查询都极为高效。

-- 存储每分钟的 PV/UV 聚合结果
CREATE TABLE visits_agg_daily
(`minute` DateTime, -- 聚合时间粒度:分钟`pv` AggregateFunction(count), -- 存储 PV 的中间状态`uv` AggregateFunction(uniq, String) -- 存储 UV 的中间状态
)
ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMMDD(minute)
ORDER BY minute;

 现在,我们创建物化视图,将源表和目标表连接起来。

CREATE MATERIALIZED VIEW visits_mv TO visits_agg_daily -- 关键:指定数据流向的目标表
AS
SELECTtoStartOfMinute(timestamp) AS minute, -- 按分钟聚合countState() AS pv, -- 计算 PV 的中间状态uniqState(user_id) AS uv -- 计算 UV 的中间状态
FROMvisits_raw -- 关键:指定监听的源表
GROUP BYminute;

代码解析

  • TO visits_agg_daily: 明确指出,计算结果将写入 visits_agg_daily 表。

  • AS SELECT ... FROM visits_raw: 定义了转换逻辑。当 visits_raw 插入新数据时,就执行这个 SELECT

  • toStartOfMinute(timestamp): 将时间戳对齐到分钟级别。

  • countState(), uniqState(user_id): 使用 -State 函数,生成与 AggregatingMergeTree 兼容的聚合中间态。

  • GROUP BY minute: 按分钟进行聚合。

正确的查询方式

SELECTminute,countMerge(pv) AS page_views,uniqMerge(uv) AS unique_visitors
FROMvisits_agg_daily
GROUP BYminute
ORDER BYminute;

结果

minutepage_viewsunique_visitors
2023/11/20 10:3043

至此,我们成功搭建了一个全自动的实时聚合系统! 后续任何写入 visits_raw 的数据,都会被 visits_mv 自动处理并更新到 visits_agg_daily 中。分析查询只需要访问 visits_agg_daily 即可。物化视图只对创建之后新插入的数据生效。如果源表在创建物化视图之前就已经存在大量数据,怎么办?

使用 POPULATE 关键字!它会在创建视图的同时,将源表中的存量数据也进行一次转换并插入目标表。

CREATE MATERIALIZED VIEW visits_mv TO visits_agg_daily
POPULATE -- <<<<<<<<<<<<<<<<
AS
SELECT ...
  • AggregatingMergeTree: 最常用,适用于需要复杂聚合(如 uniq, avg, quantile)且要求高性能的场景。

  • SummingMergeTree: 如果你的聚合需求只有“求和”与“计数”,可以使用它。它会自动合并具有相同排序键的行,将度量列相加。比 AggregatingMergeTree 更简单。

  • 普通 MergeTree: 如果你不需要预聚合,只是想对数据进行ETL(比如清洗、转换、拆分列),可以将目标表设置为普通 MergeTree

  • 删除物化视图: DROP VIEW visits_mv;

    • 注意:删除物化视图不会删除目标表 visits_agg_daily。它只是切断了自动的数据流。目标表中的数据仍然存在。

  • 暂停/恢复:

    • DETACH TABLE visits_mv; 可以临时禁用物化视图。

    • ATTACH TABLE visits_mv; 可以重新激活它。

你可以创建一个物化视图,其目标表同时是另一个物化视图的源表,形成数据处理流水线(Pipeline)

  • MV1Source Table 读取数据,聚合后写入 a_min

  • MV2 监听 a_min,当 a_min 有新数据时,MV2 将其进一步聚合到 a_hour

总结

特性普通视图 (View)物化视图 (Materialized View)
存储数据否,只是查询别名否(本身是触发器),但其结果存储在物理目标表中
数据来源查询时实时计算源表 INSERT 时触发,增量计算
查询性能等同于其定义的复杂查询极高,直接查询预计算好的小表
数据新鲜度实时准实时(有微小延迟)
资源消耗查询时消耗大写入时有额外计算开销,查询时消耗小
核心作用简化复杂查询,权限控制查询加速,实时聚合,数据ETL

何时使用物化视图?

  1. 当你有高频的、固定的聚合查询,且无法接受直接扫描原始大表的延迟时。

  2. 当需要构建实时数据看板实时报表系统时。

  3. 当需要对写入的数据流进行持续的ETL转换时。

http://www.xdnf.cn/news/16200.html

相关文章:

  • 【lucene】如何给StandardAnalyzer添加charfilter
  • P1106 删数问题 - 洛谷
  • Multiscale Structure Guided Diffusion for Image Deblurring 论文阅读
  • 用友ERP 反射xss漏洞复现(CVE-2025-2709)
  • [NLP]多电源域设计的仿真验证方法
  • Linux运维新人自用笔记(Rsync远程传输备份,服务端、邮箱和客户端配置、脚本)
  • 编译器-gcc/g++和自动化构建-make/Makefile
  • AI冲击搜索?谷歌说:恰恰相反
  • C语言第 9 天学习笔记:数组(二维数组与字符数组)
  • 优秀案例:基于python django的智能家居销售数据采集和分析系统设计与实现,使用混合推荐算法和LSTM算法情感分析
  • Java 大视界 -- 基于 Java 的大数据分布式存储在工业互联网数据管理与边缘计算协同中的创新实践(364)
  • 矩阵谱分解的证明及计算示例
  • JVM相关面试八股
  • 虚拟机docker elasticsearch启动失败
  • Elasticsearch-ik分析器
  • 三维图像识别中OpenCV、PCL和Open3D结合的主要技术概念、部分示例
  • Java设计模式-代理模式
  • 《Angular+Spring Boot:ERP前端采购销售库存协同架构解析》
  • FalconFS: Distributed File System for Large-Scale Deep Learning Pipeline——论文阅读
  • ReVQ (Quantize-then-Rectify,量化后修正)
  • [MMU] Table walk flow详解
  • IAR编辑器如何让左侧的工具栏显示出来?
  • MCP工具开发实战:打造智能体的“超能力“
  • GaussDB 逻辑备份实操
  • windows11安装wsl装Ubuntu到D盘及可视化页面,安装docker及宝塔面板
  • 初识opencv03——图像预处理2
  • Day 20:奇异值SVD分解
  • Python Day15 面向对象核心特性笔记 及 例题分析
  • 数组toString方法及类型检测修复方案
  • Linux 内核基础统简全解:Kbuild、内存分配和地址映射