普通视图
参数化视图
Materialized View
OR REPLACE 和 IF NOT EXISTS 互斥:不能同时使用,否则会报语法错误。
CREATE OR REPLACE MATERIALIZED VIEW
CREATE OR REPLACE MATERIALIZED VIEW 会以原子方式替换现有的 materialized view 及其内部存储表 (如有) 。此操作要求使用 Atomic 或 Replicated 数据库引擎。
- 不带
TO子句:旧的内部表会被删除,并创建一个新的内部表。除非指定了POPULATE,否则内部表中的现有数据将会丢失。 - 带
TO子句:仅替换视图定义;目标表及其数据不受影响。 - 兼容
REFRESH、ON CLUSTER和所有引擎选项。POPULATE仅在Atomic数据库中受支持——在Replicated数据库中会被拒绝 (请参见下方关于POPULATE的说明) 。 - 需要
CREATE VIEW和DROP VIEW特权。
CREATE OR REPLACE MATERIALIZED VIEW 仅受 Atomic 或 Replicated 数据库引擎支持。不支持 Ordinary 数据库引擎。TO [db].[table] 的 Materialized view 时,必须指定 ENGINE,即用于存储数据的表引擎。
创建带有 TO [db].[table] 的 Materialized view 时,不能同时使用 POPULATE。
Materialized view 的实现方式如下:当向 SELECT 中指定的表插入数据时,部分新插入的数据会经过该 SELECT 查询转换,结果再插入到该视图中。
ClickHouse 中的 Materialized views 在插入目标表时依据列名而不是列顺序。如果
SELECT 查询结果中缺少某些列名,ClickHouse 会使用默认值,即使该列不是 Nullable。因此,使用 Materialized views 时,较稳妥的做法是为每一列都添加别名。ClickHouse 中的 Materialized views 更像是插入触发器。如果视图查询中包含 aggregation,它只会作用于刚插入的数据批次。对源表现有数据的任何更改 (如 update、delete、drop partition 等) 都不会改变 materialized view。ClickHouse 中的 Materialized views 在发生错误时不具备确定性行为。这意味着,已经写入的块会保留在目标表中,但出错后的所有块都不会写入。默认情况下,如果推送到某个视图失败,INSERT 查询也会失败,并且某些块可能不会写入目标表。可通过 materialized_views_ignore_errors 设置更改此行为 (应在 INSERT 查询中设置) ;如果将 materialized_views_ignore_errors=true,则推送到视图时发生的任何错误都会被忽略,所有块都会写入目标表。另请注意,对 system.*_log 表而言,materialized_views_ignore_errors 默认值为 true。POPULATE,则创建视图时会将表中现有数据插入该视图,就像执行一次 CREATE TABLE ... AS SELECT ... 一样。否则,查询中只包含在视图创建后插入到表中的数据。我们不建议使用 POPULATE,因为在视图创建期间插入到表中的数据不会被插入到该视图中。
鉴于
POPULATE 的工作方式类似于 CREATE TABLE ... AS SELECT ...,因此它有以下限制:- 不支持 Replicated database
- 不支持 ClickHouse Cloud
INSERT ... SELECT。SELECT 查询可以包含 DISTINCT、GROUP BY、ORDER BY、LIMIT。请注意,相应的转换会在每个插入数据块上独立执行。例如,如果设置了 GROUP BY,数据会在插入期间进行 aggregation,但仅限于单个插入数据包内的数据,之后不会再进一步聚合。例外情况是使用可自行执行数据 aggregation 的 ENGINE,例如 SummingMergeTree。
如果 materialized view 使用 TO [db.]name 这种写法,你可以先 DETACH 该视图,对目标表执行 ALTER,然后再 ATTACH 之前已 DETACH 的视图。
请注意,materialized view 会受到 optimize_on_insert 设置的影响。数据会先合并,再插入到视图中。
视图看起来与普通表相同。例如,它们会显示在 SHOW TABLES 查询结果中。
要删除视图,请使用 DROP VIEW。不过,DROP TABLE 对 VIEW 也同样适用。
SQL 安全
DEFINER 和 SQL SECURITY 允许你指定在执行视图的底层查询时使用哪个 ClickHouse 用户。
SQL SECURITY 有三个合法取值:DEFINER、INVOKER 或 NONE。你可以在 DEFINER 子句中指定任何现有用户或 CURRENT_USER。
下表说明了从视图中查询时,哪个用户需要具备哪些权限。
请注意,无论 SQL 安全选项是什么,在所有情况下,仍然需要具备 GRANT SELECT ON <view> 才能读取该视图。
| SQL 安全选项 | 视图 | Materialized View |
|---|---|---|
DEFINER alice | alice 必须拥有该视图源表的 SELECT 权限。 | alice 必须拥有该视图源表的 SELECT 权限,以及该视图目标表的 INSERT 权限。 |
INVOKER | 用户必须拥有该视图源表的 SELECT 权限。 | 不能为 materialized view 指定 SQL SECURITY INVOKER。 |
NONE | - | - |
SQL SECURITY NONE 是一个已弃用选项。任何有权创建带有 SQL SECURITY NONE 的视图的用户,都能够执行任意查询。
因此,要使用此选项创建视图,必须具备 GRANT ALLOW SQL SECURITY NONE TO <user>。DEFINER/SQL SECURITY,则使用默认值:
如果视图在附加时未指定 DEFINER/SQL SECURITY,则 materialized view 的默认值为 SQL SECURITY NONE,普通视图的默认值为 SQL SECURITY INVOKER。
要更改现有视图的 SQL 安全设置,请使用
示例
Live View
可刷新materialized view
interval 是由若干简单时间间隔构成的序列:
- 如果指定了
APPEND,每次刷新都会将行插入表中,而不会删除现有行。该 insert 不是原子的,和普通的INSERT INTO ... SELECT查询一样。 - 否则,每次刷新都会以原子方式替换表’的现有内容。
- 没有插入触发器。当新数据插入到
SELECT中指定的表时,它不会自动推送到可刷新materialized view。相反,只有在周期性刷新或手动刷新执行时才会插入数据。 - 对
SELECT查询没有限制。表函数 (例如url()) 、视图、UNION、JOIN 都允许使用。
查询中
REFRESH ... SETTINGS 部分的 settings 是刷新设置 (例如 refresh_retries) ,与常规设置 (例如 max_threads) 不同。常规设置可以在查询末尾使用 SETTINGS 指定。刷新调度
RANDOMIZE FOR 会随机调整每次刷新的时间,例如:
REFRESH EVERY 1 MINUTE 的视图完成一次 refresh 需要 2 分钟,那么它实际上就只会每 2 分钟 refresh 一次。如果之后它变快了,能在 10 秒内完成 refresh,那么它又会恢复为每分钟 refresh 一次。 (特别地,它不会为了补上错过的 refresh 而改为每 10 秒 refresh 一次——因为并不存在这样的积压。)
此外,除非在 CREATE 查询中指定了 EMPTY,否则 materialized view 创建后会立即启动一次 refresh。如果指定了 EMPTY,则第一次 refresh 会按计划时间进行。
在 Replicated DB 中
APPEND 模式下,可以通过 SETTINGS all_replicas = 1 禁用协调。这样一来,各副本会彼此独立地执行刷新。在这种情况下,不要求使用 ReplicatedMergeTree。
在非 APPEND 模式下,仅支持协调刷新。若要使用非协调方式,请使用 Atomic database 和 CREATE ... ON CLUSTER 查询,在所有副本上创建可刷新 materialized view。
协调通过 Keeper 完成。znode 路径由 default_replica_path 服务器设置决定。
刷新依赖
DEPENDS ON 可用于同步不同表的刷新。比如,假设有一条由两个可刷新materialized view 组成的事件链:
DEPENDS ON,两个视图都会在午夜开始刷新,而 destination 通常看到的仍是 source 中昨天的数据。如果我们添加依赖关系:
destination 的刷新只会在 source 当天的刷新完成后才开始,因此 destination 将基于最新数据。
或者,也可以通过以下方式达到相同的效果:
1 HOUR 可以是任何小于 source 刷新周期的时长。依赖表的刷新频率不会高于其任一依赖项。这是在无需多次指定实际刷新周期的情况下,设置可刷新的视图事件链的一种有效方式。
再举几个示例:
REFRESH EVERY 1 DAY OFFSET 10 MINUTE(destination) 依赖于REFRESH EVERY 1 DAY(source)
如果source刷新耗时超过 10 分钟,destination就会等待它。REFRESH EVERY 1 DAY OFFSET 1 HOUR依赖于REFRESH EVERY 1 DAY OFFSET 23 HOUR
与上面类似,即使对应的刷新发生在不同的自然日也是如此。destination在第X+1天的刷新会等待source在第X天的刷新 (如果其耗时超过 2 小时) 。REFRESH EVERY 2 HOUR依赖于REFRESH EVERY 1 HOUR
2 HOUR的刷新会在每隔一小时对应的1 HOUR刷新完成后发生,例如在午夜刷新之后, 然后在凌晨 2 点刷新之后,依此类推。REFRESH EVERY 1 MINUTE依赖于REFRESH EVERY 2 HOUR
destination会在每次source刷新后刷新一次,也就是每 2 小时一次。这里的1 MINUTE实际上会被忽略。REFRESH AFTER 1 HOUR依赖于REFRESH AFTER 1 HOUR
目前不建议这样做。
DEPENDS ON 仅适用于可刷新materialized view 之间。在 DEPENDS ON 列表中列出普通表会导致该视图永远不会刷新 (可使用 ALTER 移除依赖关系,参见 更改刷新参数) 。刷新设置
refresh_retries- 刷新查询因异常失败时的重试次数。如果所有重试都失败,则跳过并等待下一个计划刷新时间。0 表示不重试,-1 表示无限重试。默认值:2。refresh_retry_initial_backoff_ms- 如果refresh_retries不为 0,首次重试前的延迟时间。此后每次重试的延迟都会翻倍,直到达到refresh_retry_max_backoff_ms。默认值:100 毫秒。refresh_retry_max_backoff_ms- 刷新尝试之间延迟时间指数增长的上限。默认值:60000 毫秒 (1 分钟) 。all_replicas- 在带有APPEND的 Replicated database 中,控制是让所有副本独立刷新,还是在每个计划时间点仅由一个副本刷新。视图创建后不可更改。默认值:false。prefer_dependency_replica- 当视图带有DEPENDS ON时,执行父级刷新的副本会优先执行依赖刷新;其他副本则会将尝试延迟prefer_dependency_replica_delay_ms。与SharedMergeTree搭配使用时,此设置可避免因复制延迟而导致依赖刷新事件链中出现数据缺失。默认值:false。prefer_dependency_replica_delay_ms- 启用prefer_dependency_replica时,非优先副本在尝试执行依赖刷新前的等待时间。默认值:2000 毫秒。
更改刷新参数
ALTER TABLE ... MODIFY REFRESH 更改现有可刷新materialized view的刷新参数:
EVERY 或 AFTER) 是必填项:该语句始终会用指定内容替换所有刷新参数——包括调度、RANDOMIZE FOR、DEPENDS ON 和刷新设置。凡是未指定的内容,都会重置为默认值 (设置) 或被移除 (依赖项、随机化) 。
-
如果只想修改刷新设置 (例如
refresh_retries) ,请重复现有调度: -
materialized view 不支持
ALTER TABLE ... MODIFY SETTING refresh_retries = ...;必须通过MODIFY REFRESH进行修改。 -
不支持添加或移除
APPEND。 -
all_replicas设置在创建后无法更改。
其他操作
system.view_refreshes 中查看。具体而言,其中包含刷新进度 (如果正在运行) 、上次和下次刷新时间,以及刷新失败时的异常信息。
如需手动停止、启动、触发或取消刷新,请使用 SYSTEM STOP|START|REFRESH|WAIT|CANCEL VIEW。
如需等待刷新完成,请使用 SYSTEM WAIT VIEW。这在创建视图后等待首次刷新时尤其有用。
顺带一提:刷新查询可以从正在刷新的视图中读取数据,并看到刷新前版本的数据。这意味着你可以实现 Conway’s game of life:https://pastila.nl/?00021a4b/d6156ff819c83d490ad2dcec05676865#O0LGWTO7maUQIA4AcGUtlA==
Window View
这是一项 Experimental 功能,未来的发行版中可能会发生向后不兼容的变更。使用 allow_experimental_window_view 设置来启用 window view 和
WATCH 查询。输入命令 set allow_experimental_window_view = 1。WATCH 查询推送通知。
创建窗口视图与创建 MATERIALIZED VIEW 类似。窗口视图需要一个内部存储引擎来保存中间数据。可以使用 INNER ENGINE 子句指定内部存储引擎;如果未指定,窗口视图默认使用 AggregatingMergeTree 作为内部引擎。
创建不带 TO [db].[table] 的窗口视图时,必须指定 ENGINE——即用于存储数据的表引擎。
时间窗口函数
时间属性
time_attr 设置为表中的某一列,或使用函数 now(),来定义处理时间属性。以下查询创建了一个使用处理时间的 窗口视图。
WATERMARK 语法支持事件时间处理。
窗口视图提供三种 watermark 策略:
STRICTLY_ASCENDING:发出截至当前已观测到的最大时间戳作为 watermark。时间戳小于该最大时间戳的行不属于迟到数据。ASCENDING:发出截至当前已观测到的最大时间戳减 1 作为 watermark。时间戳等于或小于该最大时间戳的行不属于迟到数据。BOUNDED:WATERMARK=INTERVAL。发出 watermark,其值为已观测到的最大时间戳减去指定延迟。
WATERMARK 创建窗口视图:
ALLOWED_LATENESS=INTERVAL 来支持处理迟到事件。下面是一个处理迟到事件的示例:
ALTER TABLE ... MODIFY QUERY 语句来修改窗口视图中定义的 SELECT 查询。新的 SELECT 查询产生的数据结构,无论是否包含 TO [db.]name 子句,都应与原始 SELECT 查询保持一致。请注意,当前窗口中的数据将会丢失,因为中间状态无法复用。
监视新窗口
TO 语法将结果输出到表中。
LIMIT,以设置在终止查询前接收更新的次数。EVENTS 子句可用于获取 WATCH 查询的简写形式:此时返回的不再是查询结果,而仅是最新的查询水位线。
设置
window_view_clean_interval:窗口视图的清理间隔,单位为秒,用于释放过期数据。系统会根据系统时间或WATERMARK配置保留尚未被完全触发的窗口,其他数据将被删除。window_view_heartbeat_interval:心跳间隔,单位为秒,用于表明 watch 查询仍处于活动状态。wait_for_window_view_fire_signal_timeout:在事件时间处理中等待窗口视图触发信号的超时时间。
示例
data 的日志表中每 10 秒内的点击日志数量,其表结构如下:
WATCH 查询获取结果。
data 时,
WATCH 查询应输出如下结果:
TO 语法将输出写入另一张表。
*window_view*) 。
窗口视图用法
- 监控:按时间对指标日志进行聚合和计算,并将结果输出到目标表。仪表板可以将目标表用作源表。
- 分析:在时间窗口内自动聚合并预处理数据。这在分析大量日志时非常有用。预处理可消除多个查询中的重复计算,并降低查询延迟。
临时视图
- 会话生命周期 临时视图仅在当前会话期间存在。会话结束后会自动删除。
- 无数据库 不能使用数据库名限定临时视图。它存在于数据库之外 (会话命名空间) 。
-
不复制 / 不支持 ON CLUSTER
临时对象仅在当前会话内有效,不能通过
ON CLUSTER创建。 - 名称解析 如果临时对象 (表或视图) 与持久对象同名,且某个查询在不带数据库名的情况下引用该名称,则会使用临时对象。
-
逻辑对象 (无存储)
临时视图仅存储其
SELECT文本 (内部使用View存储) 。它不会持久化数据,也不接受INSERT。 -
Engine 子句
无需指定
ENGINE;如果写为ENGINE = View,也会被忽略,并视为相同的逻辑视图。 -
安全 / 特权
创建临时视图需要
CREATE TEMPORARY VIEW特权,而CREATE VIEW会隐式授予该特权。 -
SHOW CREATE
使用
SHOW CREATE TEMPORARY VIEW view_name;可输出临时视图的 DDL。
语法
OR REPLACE 不支持用于临时视图 (与临时表一致) 。如果你需要“替换”临时视图,请先将其删除,再重新创建。
示例
不允许的用法 / 限制
CREATE OR REPLACE TEMPORARY VIEW ...→ 不允许 (请使用DROP+CREATE) 。CREATE TEMPORARY MATERIALIZED VIEW .../WINDOW VIEW→ 不允许。CREATE TEMPORARY VIEW db.view AS ...→ 不允许 (不支持数据库限定) 。CREATE TEMPORARY VIEW view ON CLUSTER 'name' AS ...→ 不允许 (临时对象仅在当前会话内有效) 。POPULATE、REFRESH、TO [db.table]、内部引擎以及所有 MV 专用子句 → 不适用于临时视图。
关于分布式查询的说明
Memory) ,其数据在分布式查询执行期间也可以像临时表一样传送到远程服务器。