【Hive实战】Hive 物化视图

Hive 物化视图 (Materialized views)

始于Hive3.0.0

目标

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

在Apache Hive 3.0.0中,主要是在项目中引入物化视图和基于这些物化的自动查询重写。特别是,物化视图可以原生存储在Hive中,也可以使用自定义存储处理程序存储在其他系统中(如Druid),而且它们可以无缝地利用Hive新的令人兴奋的功能,如LLAP加速。然后,优化器依靠Apache Calcite为包含投影、过滤器、连接和聚合操作的查询表达式自动生成全部和部分重写。

在本文档中,我们详细介绍了Hive中物化视图的创建和管理,通过一些示例描述了当前重写算法的覆盖范围,并解释了Hive如何控制物化视图生命周期的重要方面,如数据的新鲜度。

Hive中物化视图的管理

在本节中,我们将介绍目前Hive中用于物化视图管理的主要操作。

创建物化视图

在Hive中创建物化视图的语法与CTAS语句语法非常相似,支持分区列、自定义存储处理程序或传递表属性等常见功能。

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中的物化视图:

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;

物化视图管理的其他操作

目前,我们支持以下操作来帮助管理Hive中的物化视图:

-- 删除物化视图
DROP MATERIALIZED VIEW [db_name.]materialized_view_name;
-- 查询物化视图 (with optional filters)
SHOW MATERIALIZED VIEWS [IN database_name] ['identifier_with_wildcards’];
-- 查询物化视图的主要信息
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的一部分,它支持包含TableScanProjectFilterJoinAggregate操作符的查询。关于重写覆盖面的更多信息可以在calcite找到。

在下文中,我们包括几个例子,简要说明了不同的重写方式。

示例

考虑由以下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';

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

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关键字,所以它们对优化器是可见的。现在假设我们想要创建一个非规范化数据库内容的物化(考虑dim是我们将经常查询的维度集合):

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;

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

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操作时的增量重建,而UPDATE和DELETE操作会强制重建物化视图。

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

  • 物化视图应该只使用事务性表,无论是微观管理还是ACID。
  • 如果物化视图定义包含Group By子句,物化视图应该存储在ACID表中,因为它需要支持MERGE操作。对于由Scan-Project-Filter-Join组成的物化视图定义,不存在这个限制。

重建操作会获得对物化视图的独占写锁,也就是说,对于一个给定的物化视图,在给定时间内只能执行一个重建操作。

物化视图的生命周期

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

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

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

参数值也可以被具体的物化视图重写,只需在创建物化时将其设置为表属性即可。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

顧棟

若对你有帮助,望对作者鼓励一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值