Materialized views

Version information

Hive 3.0.0 中引入了对物化视图的支持。

Introduction

该页面记录了为 Apache Hive 中的支持实例化视图所做的工作。

Objectives

传统上,用于加速数据仓库中查询处理的最强大技术之一是对相关摘要或实例化视图进行预计算。

Apache Hive 3.0.0 中引入的初始实现侧重于引入物化视图和基于项目中的那些物化的自动查询重写。特别地,物化视图可以使用自定义存储处理程序本地存储在 Hive 或其他系统(例如 Druid)中,并且它们可以无缝地利用新的令人兴奋的 Hive 功能(例如 LLAP 加速)。然后,优化器依靠 Apache Calcite 为包括查询,过滤,联接和聚合操作在内的大量查询表达式自动生成全部和部分重写。

在本文档中,我们提供有关 Hive 中物化视图创建和 Management 的详细信息,并通过一些示例描述重写算法的当前覆盖范围,并说明 Hive 如何控制物化视图生命周期的重要方面,例如数据的新鲜度。

在 Hive 中 Management 实例化视图

在本节中,我们介绍了 Hive 中当前用于实体化视图 Management 的主要操作。

实例化视图的创建

在 Hive 中创建实例化视图的语法与CTAS statement语法非常相似,支持公用功能,例如分区列,自定义存储处理程序或传递表属性。

CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db_name.]materialized_view_name
  [DISABLE REWRITE]
  [COMMENT materialized_view_comment]
  [PARTITIONED ON (col_name, ...)]
  [CLUSTERED ON (col_name, ...) | DISTRIBUTED ON (col_name, ...) SORTED ON (col_name, ...)]
  [
    [ROW FORMAT row_format]
    [STORED AS file_format]
      | STORED BY 'storage.handler.class.name' [WITH SERDEPROPERTIES (...)]
  ]
  [LOCATION hdfs_path]
  [TBLPROPERTIES (property_name=property_value, ...)]
AS
<query>;

创建实例化视图时,其内容将由语句中执行查询的结果自动填充。物化视图创建语句是原子的,这意味着在填充所有查询结果之前,其他用户看不到物化视图。

默认情况下,实例化视图可用于优化程序重写查询,而DISABLE REWRITE选项可用于在实例化视图创建时更改此行为。

在实例化视图创建语句中未指定的 SerDe 和存储格式的默认值(它们是可选的)分别使用配置属性hive.materializedview.serdehive.materializedview.fileformat指定。

实例化视图可以使用自定义存储处理程序存储在外部系统中,例如Druid。例如,以下语句创建存储在 Druid 中的实例化视图:

Example:

CREATE MATERIALIZED VIEW druid_wiki_mv
STORED AS 'org.apache.hadoop.hive.druid.DruidStorageHandler'
AS
SELECT __time, page, user, c_added, c_removed
FROM src;

物化视图 Management 的其他操作

当前,我们支持以下有助于在 Hive 中 Management 实例化视图的操作:

-- Drops a materialized view
DROP MATERIALIZED VIEW [db_name.]materialized_view_name;
-- Shows materialized views (with optional filters)
SHOW MATERIALIZED VIEWS [IN database_name] ['identifier_with_wildcards'];
-- Shows information about a specific materialized view
DESCRIBE [EXTENDED | FORMATTED] [db_name.]materialized_view_name;

这些操作的功能将在将来扩展,并且可能会添加更多操作。

基于视图的物化查询重写

创建实例化视图后,优化器将能够利用其定义语义来使用实例化视图自动重写传入的查询,从而加快查询的执行速度。

可以使用hive.materializedview.rewriting配置属性(默认值为true)全局启用和禁用重写算法。此外,用户可以有选择地启用/禁用实例化视图以进行重写。回想一下,默认情况下,实例化视图已启用,可在创建时进行重写。若要更改该行为,可以使用以下语句:

ALTER MATERIALIZED VIEW [db_name.]materialized_view_name ENABLE|DISABLE REWRITE;

重写算法是 Apache Calcite 的一部分,它支持包含 TableScan,Project,Filter,Join 和 Aggregate 运算符的查询。有关重写范围的更多信息,请参见here。在下面的内容中,我们将提供一些示例来简要说明不同的重写。

Example 1

考虑以下 DDL 语句创建的数据库模式:

CREATE TABLE emps (
  empid INT,
  deptno INT,
  name VARCHAR(256),
  salary FLOAT,
  hire_date TIMESTAMP)
STORED AS ORC
TBLPROPERTIES ('transactional'='true');

CREATE TABLE depts (
  deptno INT,
  deptname VARCHAR(256),
  locationid INT)
STORED AS ORC
TBLPROPERTIES ('transactional'='true');

假设我们要经常获取有关 2016 年之后按不同时期粒度聘用的员工及其部门的信息。我们可以创建以下物化视图:

CREATE MATERIALIZED VIEW mv1
AS
SELECT empid, deptname, hire_date
FROM emps JOIN depts
  ON (emps.deptno = depts.deptno)
WHERE hire_date >= '2016-01-01';

然后,以下查询提取有关 Hive 2018 年第一季度雇用的员工的信息:

SELECT empid, deptname
FROM emps
JOIN depts
  ON (emps.deptno = depts.deptno)
WHERE hire_date >= '2018-01-01'
    AND hire_date <= '2018-03-31';

Hive 将能够使用实例化视图重写传入的查询,包括在实例化扫描之上的补偿谓词。尽管重写发生在代数级别,但为说明此示例,我们包含与 Hive 用来回答传入查询的mv等效的 SQL 语句:

SELECT empid, deptname
FROM mv1
WHERE hire_date >= '2018-01-01'
    AND hire_date <= '2018-03-31';

Example 2

对于第二个示例,请考虑基于以下 DDL 语句创建的SSB benchmark的星型模式:

CREATE TABLE `customer`(
  `c_custkey` BIGINT, 
  `c_name` STRING, 
  `c_address` STRING, 
  `c_city` STRING, 
  `c_nation` STRING, 
  `c_region` STRING, 
  `c_phone` STRING, 
  `c_mktsegment` STRING,
  PRIMARY KEY (`c_custkey`) DISABLE RELY)
STORED AS ORC
TBLPROPERTIES ('transactional'='true');

CREATE TABLE `dates`(
  `d_datekey` BIGINT, 
  `d_date` STRING, 
  `d_dayofweek` STRING, 
  `d_month` STRING, 
  `d_year` INT, 
  `d_yearmonthnum` INT, 
  `d_yearmonth` STRING, 
  `d_daynuminweek` INT,
  `d_daynuminmonth` INT,
  `d_daynuminyear` INT,
  `d_monthnuminyear` INT,
  `d_weeknuminyear` INT,
  `d_sellingseason` STRING,
  `d_lastdayinweekfl` INT,
  `d_lastdayinmonthfl` INT,
  `d_holidayfl` INT,
  `d_weekdayfl`INT,
  PRIMARY KEY (`d_datekey`) DISABLE RELY)
STORED AS ORC
TBLPROPERTIES ('transactional'='true');

CREATE TABLE `part`(
  `p_partkey` BIGINT, 
  `p_name` STRING, 
  `p_mfgr` STRING, 
  `p_category` STRING, 
  `p_brand1` STRING, 
  `p_color` STRING, 
  `p_type` STRING, 
  `p_size` INT, 
  `p_container` STRING,
  PRIMARY KEY (`p_partkey`) DISABLE RELY)
STORED AS ORC
TBLPROPERTIES ('transactional'='true');

CREATE TABLE `supplier`(
  `s_suppkey` BIGINT, 
  `s_name` STRING, 
  `s_address` STRING, 
  `s_city` STRING, 
  `s_nation` STRING, 
  `s_region` STRING, 
  `s_phone` STRING,
  PRIMARY KEY (`s_suppkey`) DISABLE RELY)
STORED AS ORC
TBLPROPERTIES ('transactional'='true');

CREATE TABLE `lineorder`(
  `lo_orderkey` BIGINT, 
  `lo_linenumber` int, 
  `lo_custkey` BIGINT not null DISABLE RELY,
  `lo_partkey` BIGINT not null DISABLE RELY,
  `lo_suppkey` BIGINT not null DISABLE RELY,
  `lo_orderdate` BIGINT not null DISABLE RELY,
  `lo_ordpriority` STRING, 
  `lo_shippriority` STRING, 
  `lo_quantity` DOUBLE, 
  `lo_extendedprice` DOUBLE, 
  `lo_ordtotalprice` DOUBLE, 
  `lo_discount` DOUBLE, 
  `lo_revenue` DOUBLE, 
  `lo_supplycost` DOUBLE, 
  `lo_tax` DOUBLE, 
  `lo_commitdate` BIGINT, 
  `lo_shipmode` STRING,
  PRIMARY KEY (`lo_orderkey`) DISABLE RELY,
  CONSTRAINT fk1 FOREIGN KEY (`lo_custkey`) REFERENCES `customer_n1`(`c_custkey`) DISABLE RELY,
  CONSTRAINT fk2 FOREIGN KEY (`lo_orderdate`) REFERENCES `dates_n0`(`d_datekey`) DISABLE RELY,
  CONSTRAINT fk3 FOREIGN KEY (`lo_partkey`) REFERENCES `ssb_part_n0`(`p_partkey`) DISABLE RELY,
  CONSTRAINT fk4 FOREIGN KEY (`lo_suppkey`) REFERENCES `supplier_n0`(`s_suppkey`) DISABLE RELY)
STORED AS ORC
TBLPROPERTIES ('transactional'='true');

如您所见,我们使用RELY关键字为数据库声明了多个完整性约束,以便优化程序可以看到它们。现在假设我们要创建一个实现对数据库内容进行非规范化的实现(考虑将dims作为我们经常查询的一组维):

CREATE MATERIALIZED VIEW mv2
AS
SELECT <dims>,
    lo_revenue,
    lo_extendedprice * lo_discount AS d_price,
    lo_revenue - lo_supplycost
FROM customer, dates, lineorder, part, supplier
WHERE lo_orderdate = d_datekey
    AND lo_partkey = p_partkey
    AND lo_suppkey = s_suppkey
    AND lo_custkey = c_custkey;

上面的实例化视图可以加速在数据库中不同表之间执行联接的查询。例如,考虑以下查询:

SELECT SUM(lo_extendedprice * lo_discount)
FROM lineorder, dates
WHERE lo_orderdate = d_datekey
  AND d_year = 2013
  AND lo_discount between 1 and 3;

尽管查询未使用实例化视图中存在的所有表,但可以使用实例化视图进行查询,因为mv2中的联接保留了lineorder表中的所有行(由于完整性约束,我们知道这一点)。因此,该算法产生的基于视图的物化重写如下:

SELECT SUM(d_price)
FROM mv2
WHERE d_year = 2013
  AND lo_discount between 1 and 3;

Example 3

对于第三个示例,请考虑具有单个表的数据库模式,该表存储由给定网站产生的编辑事件:

CREATE TABLE `wiki` (
  `time` TIMESTAMP, 
  `page` STRING, 
  `user` STRING, 
  `characters_added` BIGINT,
  `characters_removed` BIGINT)
STORED AS ORC
TBLPROPERTIES ('transactional'='true');

对于此示例,我们将使用 Druid 存储实例化视图。假设我们要在表上执行查询,但是,对于超过一分钟的时间粒度级别的事件,我们不感兴趣。我们可以创建以下实例化视图,按分钟汇总事件:

CREATE MATERIALIZED VIEW mv3
STORED BY 'org.apache.hadoop.hive.druid.DruidStorageHandler'
AS
SELECT floor(time to minute) as `__time`, page,
    SUM(characters_added) AS c_added,
    SUM(characters_removed) AS c_removed
FROM wiki
GROUP BY floor(time to minute), page;

然后,假设我们需要回答以下查询,该查询提取每月添加的字符数:

SELECT floor(time to month),
    SUM(characters_added) AS c_added
FROM wiki
GROUP BY floor(time to month);

Hive 将能够通过将实例化视图的数据汇总到月份粒度并投影查询结果所需的信息来使用mv3重写传入的查询:

SELECT floor(time to month),
    SUM(c_added)
FROM mv3
GROUP BY floor(time to month);

实例化视图维护

当实例化视图使用的源表中的数据发生更改时,例如,插入新数据或修改现有数据时,我们将需要刷新实例化视图的内容,以使其与这些更改保持最新。当前,物化视图的重建操作需要由用户触发。特别是,用户应执行以下语句:

ALTER MATERIALIZED VIEW [db_name.]materialized_view_name REBUILD;

Hive 支持增量视图维护,即仅刷新受原始源表中的更改影响的数据。增量视图维护将减少重建步骤的执行时间。此外,它将为物化视图中的现有数据保留 LLAP 缓存。

默认情况下,Hive 将尝试以增量方式重建实例化视图,如果不可能的话,将退回到完全重建。当前实现仅在源表上执行INSERT操作时支持增量重建,而UPDATEDELETE操作将强制对物化视图进行完全重建。

要执行增量维护,应满足以下条件:

  • 物化视图应仅使用微 Management 表或 ACID 事务表。

  • 如果实例化视图定义包含 Group By 子句,则该实例化视图应存储在 ACID 表中,因为它需要支持 MERGE 操作。对于包含 Scan-Project-Filter-Join 的物化视图定义,此限制不存在。

重建操作需要在物化视图上获得排他写锁定,即,对于给定的物化视图,在给定的时间只能执行一个重建操作。

物化视图生命周期

默认情况下,一旦物化视图的内容过期,该物化视图将不用于自动查询重写。

但是,在某些情况下,可以接受陈旧的数据,例如,如果物化视图使用非事务表,因此我们无法验证其内容是否过时,但是我们仍然要使用自动重写。在这种情况下,我们可以结合定期运行(例如每 5 分钟一次)的重建操作,并使用hive.materializedview.rewriting.time.window配置参数来定义实例化视图数据所需的新鲜度,例如:

SET hive.materializedview.rewriting.time.window=10min;

只要在创建实例化时将参数值设置为表格属性,也可以通过具体的实例化视图覆盖该参数值。

未解决的问题(JIRA)

||
||
|Key|Summary|T|Created|Updated|Due|Assignee|Reporter|P|Status|Resolution|

Loading...

Refresh