ByteHouse 数据库中的视图(View),除了 普通视图(Normal View) 以外,还有一种 物化视图(Materialized View)。
物化视图的工作原理基于查询重写机制和数据更新。查询重写机制对应用透明,数据库引擎优化器会自动选择合适的物化视图进行查询重写,无需修改原有查询语句。同时,物化视图提供自动刷新机制,确保数据的及时性和准确性。
在大数据环境下,物化视图特别适用于数据仓库和查询引擎中频繁执行的复杂查询场景。它通过以空间换时间的方式,提高查询效率,简化查询逻辑,并显著降低大型表上复杂查询的资源消耗。作为数据查询的加速器,物化视图在保持数据实时性的同时,极大地增强了数据分析效率。它优化了数据仓库中的大规模复杂查询处理,提升了整体的数据分析体验。
总的来说,物化视图通过预计算和存储查询结果,结合自动的查询重写和数据更新机制,在不改变原有查询语句的情况下,有效提升了数据库查询性能,为大数据环境下的复杂查询场景提供了强有力的支持。
视图类型 | 单表聚合 | 多表关联 | 查询改写 | 刷新策略 | 数据导入影响 |
---|---|---|---|---|---|
异步物化视图 | 是 | 是 | 是 | 异步刷新 | 无 |
同步物化视图 | 是 | 否 | 是 | 导入同步刷新 | 有 |
特点:
优势:
特点:
优势:
总的来说,同步视图和异步视图各有其优势和适用场景。选择哪种类型的物化视图,需要根据具体的业务需求、数据特征和系统资源情况来权衡。在实际应用中,可能会同时使用这两种类型的视图来满足不同的需求。
同步物化视图的本质就是类似一种触发器,当基表有数据写入,会触发视图执行定义的 SQL,写入另外一张表,更新粒度是导入基表的数据块,基表与视图目的表同时写入成功事务才能结束,由此可以看出同步视图会影响基表的导入性能,而且只能支持单表的场景,对于基表通过merge进行数据合并的场景也不能支持,因为基表变化不能传导视图目的表,不能支持类似unique table作为基表。
目前在 ByteHouse 根据物化视图的用途分为如下使用场景:
--创建同步视图 CREATE MATERIALIZED VIEW [IF NOT EXISTS] [database.]<mv_name> [TO [database.]<target_name>] AS <query_statement> [SETTINGS <mv_query_settings>]; --手动刷新视图 REFRESH MATERIALIZED VIEW db_name.mv_name [PARTITION partition_name] [WHERE predicate_expr] [ASYNC | SYNC];
如果存在如上要求,推荐使用异步物化视图。
下面以一个行为分析系统的事件表来说明上述视图的使用方法。
--创建数据库 create database mv; --数据源表 CREATE TABLE mv.events( app_id UInt32, server_time UInt64, event_name String, uid UInt64, cost UInt64, duration UInt64, event_date Date ) ENGINE = CnchMergeTree PARTITION BY toDate(event_date) ORDER BY (app_id, uid, event_name);
Aggregate聚合视图是物化视图最为常用的一种场景,基于特定的聚合查询对源数据抽取存为物化视图,由于聚合查询已经聚合为中间数据状态,查询视图会减少聚合计算,提高查询性能,后续的查询能命中视图,引擎对原始查询进行改写,直接查询聚合视图表。下面看具体的场景
--视图目标表 CREATE TABLE mv.events_aggregation ( app_id UInt32, event_name String, event_date Date, sum_cost AggregateFunction(sum, UInt64), max_duration AggregateFunction(max, UInt64) ) ENGINE = CnchAggregatingMergeTree() PARTITION BY toDate(event_date) ORDER BY (app_id, event_name, event_date); --视图定义 CREATE MATERIALIZED VIEW mv.events_aggregate_view to mv.events_aggregation (app_id UInt32, event_name String, event_date Date, sum_cost AggregateFunction(sum, UInt64), max_duration AggregateFunction(max, UInt64)) AS SELECT app_id, event_name, event_date, sumState(cost) AS sum_cost, maxState(duration) AS max_duration FROM mv.events GROUP BY app_id, event_name, event_date;
---明细表 insert into table mv.events(app_id, server_time, event_name, uid, cost, duration, event_date) values (1, 1642149961, 'show', 121245, 3454, 64, '2022-06-14'); insert into table mv.events(app_id, server_time, event_name, uid, cost, duration, event_date) values (2, 1642149961 , 'send', 2345, 476, 64, '2022-06-14'); insert into table mv.events(app_id, server_time, event_name, uid, cost, duration, event_date) values (3, 1642150683, 'show', 544545, 87, 5434, '2022-06-14'); insert into table mv.events(app_id, server_time, event_name, uid, cost, duration, event_date) values (3, 1642150683, 'show', 544545, 930, 232, '2022-06-14'); insert into table mv.events(app_id, server_time, event_name, uid, cost, duration, event_date) values (4, 1642150683, 'slide', 234545, 123, 98, '2022-06-14'); insert into table mv.events(app_id, server_time, event_name, uid, cost, duration, event_date) values (5, 1642150683, 'click', 131312, 2644, 26, '2022-06-14');
在业务频繁迭代的场景,经常需要使用非主键过滤条件进行查询,但是主表的主键顺序又不能修改,基于这种需求,可以定义物化视图来修改主键顺序,根据业务需求裁切部分列或者根据某些条件过滤数据,来产出视图。
--视图目标表 CREATE TABLE mv.events_normal ( app_id UInt32, event_name String, event_date Date, uid UInt64, cost UInt64 ) ENGINE = CnchMergeTree() PARTITION BY toDate(event_date) ORDER BY (uid, event_name); --视图定义 CREATE MATERIALIZED VIEW mv.events_normal_view to mv.events_normal (app_id UInt32, event_name String, event_date Date, uid UInt64, cost UInt64) AS SELECT app_id, event_name, event_date, uid, cost FROM mv.events;
实时消费以Kafka的消费为例,视图的SQL定义需要从consumer获取数据,可以全部获取作为明细表,可以进行聚合,过滤,投影等操作,可以是并联视图,或者串联视图。
--实时消费consumer表定义 CREATE TABLE mv.events_consumer ( app_id UInt32, server_time UInt64, event_name String, uid UInt64, cost UInt64, duration UInt64, event_date Date ) ENGINE = CnchKafka() SETTINGS kafka_broker_list = 'XXXX:9092', kafka_topic_list = 'ch_qa_cnch_staging_yg', kafka_group_name = 'events_consumer_group', kafka_format = 'JSONEachRow', kafka_row_delimiter = '\n', kafka_num_consumers = 5, kafka_max_block_size = 65536; ---明细表视图定义 CREATE MATERIALIZED VIEW mv.events_real_all_view to mv.events ( app_id UInt32, server_time UInt64, event_name String, uid UInt64, cost UInt64, duration UInt64, event_date Date ) AS SELECT * FROM mv.events_consumer; ---聚合表视图定义 CREATE MATERIALIZED VIEW mv.events_real_aggregate_view to mv.events_aggregation (app_id UInt32, event_name String, event_date Date, sum_cost AggregateFunction(sum, UInt64), max_duration AggregateFunction(max, UInt64)) AS SELECT app_id, event_name, event_date, sumState(cost) AS sum_cost, maxState(duration) AS max_duration FROM mv.events_consumer GROUP BY app_id, event_name, event_date; --normal表实时消费表 CREATE MATERIALIZED VIEW mv.events_real_normal_view to mv.events_normal (app_id UInt32, event_name String, event_date Date, sum_cost AggregateFunction(sum, UInt64), max_duration AggregateFunction(max, UInt64)) AS SELECT app_id, event_name, event_date, uid, cost FROM mv.events_consumer where uid = 5434;