apache-hive / 3.1.1 / reference / ViewDev.html

Hive Views

Use Cases

视图(http://issues.apache.org/jira/browse/HIVE-972)是 DBMS 的标准功能,其用法已广为人知。一个典型的用例可能是在现有一组不一致命名的表之上创建一个具有一致实体/属性命名方案的接口层,而不必由于对表的直接修改而导致中断。更高级的用例将涉及 sched 义的过滤器,联接,聚合等,以简化最终用户的查询构造,并在 ETL 管道内共享通用定义。

Scope

至少,我们要

  • 在 SQL 语言级别添加可查询的视图支持(作用域的详细信息正在下面的“问题”部分中讨论)

  • 不支持可更新的视图(请参见Updatable Views建议)

  • 确保视图及其定义出现在当前可以枚举/搜索/描述表的任何地方

  • 适当时,提供其他元数据以允许将视图与表区分开

除此之外,我们可能希望

  • 公开有关视图定义和依赖关系的元数据(在表级别或列级别),使元数据驱动工具可以使用它们

Syntax

CREATE VIEW [IF NOT EXISTS] view_name [(column_name [COMMENT column_comment], ...) ]
[COMMENT table_comment]
AS SELECT ...

DROP VIEW view_name

Implementation Sketch

由于 Hive 已经支持 FROM 子句中的子选择,因此视图实现的基础非常容易。

  • 对于 CREATE VIEW v AS view-def-select ,我们将 SemanticAnalyzer 扩展为与 CREATE TABLE t AS select 相似,除了我们实际上不执行查询(在生成计划后停止)之外。必须执行所有计划生成(即使我们实际上并不打算执行计划),因为当前一些验证(例如类型兼容性检查)仅在计划生成期间执行。成功验证之后,视图的文本将保存在 metastore 中(最简单的方法是从解析器的令牌流中截取文本,但是这种方法会引入下面的问题部分所述的问题)。

  • 对于 select ... from view-reference ,我们在 SemanticAnalyzer.getMetaData 中检测视图引用,从 metastore 加载其定义的文本,将其解析回 AST,准备一个 QBExpr 来保存它,然后将其插入引用查询的 QB 中,得到一棵等效于 select ... from(view-def-select) 的树;然后可以在组合树上执行计划生成。

Issues

其中一些与功能/范围有关;其他与实施方法有关。欢迎大家发表意见。

存储的视图定义

在 SQL:200n 中,视图定义应该在创建时冻结,因此,如果视图定义为 select * from t,其中 t 是具有两列 a 和 b 的表,则稍后请求选择*从视图中看,即使稍后将新列 c 添加到表中,也应仅返回 a 和 b 列。大多数 DBMS 产品均可正确实现此功能。

视图定义中的其他类型的引用也存在类似的问题;例如,如果可以限定表或函数名称,则应在创建视图时绑定引用。

要实现这一点,通常需要将视图定义扩展为显式形式,而不是直接存储原始视图定义文本。这样做可能需要为 AST 模型添加“非解析”支持(将在对象名称解析发生后应用),这是当前不存在的(通常也很有用)。

但是,同时存储扩展形式和原始视图定义文本也可能对 DESCRIBE 可读性和功能性都很有用(请参阅稍后的 ALTER VIEW v RECOMPILE)。

2010 年 1 月 7 日更新 :我没有采用对 AST 模型的全面未解析支持,而是采用了与解析器相关的快捷方式。 ANTLR 的 TokenRewriteStream 提供了一种方法来用文本替换原始令牌流中的令牌子序列,然后重新生成已解析文本的转换版本。因此,在列解析期间,我们将诸如“ t.*”的表达式 Map 到替换文本“ t.c1,t.c2,t.c3”。然后,一旦所有列均已解决,我们便使用这些 Map 的替换项重新生成视图定义。同样,不合格的列引用(例如“ c”)将替换为合格的引用“ t.c”。其余的解析文本保持不变。

如果将来我们需要执行更剧烈的(基于 AST 的)重写作为视图扩展的一部分,则此方法将失效。

Metastore Modeling

为了允许保存视图定义,将需要扩展元存储模型。要解决的一个重要问题是是通过继承还是作为特殊表将鞋拔视图建模。

使用继承模型,视图和基表将共享一个公共 Base Class(由于缺少更好的用语,因此在 Common Warehouse 元模型中遵循惯例在此称为 ColumnSet):

对于一个视图,大多数存储 Descriptors(除列名和类型之外的所有 Descriptors)都是不相关的,因此可以通过这种区分进一步完善该模型。

视图名称和表名称在唯一性方面共享相同的名称空间(即,您的表和视图不能具有相同的名称),因此名称键唯一性需要在 Base Class 级别上指定。

或者,如果我们选择避免继承,则可以只向现有的 Table 类添加一个新的 viewText 属性(将其保留为基本表空):

(将视图定义存储为表属性可能不起作用,因为属性值限制为 VARCHAR(767),并且视图定义的长度可能更长,因此我们需要使用 LOB。)

两种方法的比较:

Inheritance Model Flat Model
JDO Support 需要研究继承如何达到我们的目的 Nothing special
来自现有代码/工具的元数据查询 现有的表查询将不包括结果视图;那些需要修改的必须改为引用 Base Class 现有的表格查询将在结果中包含视图;那些不应该的将需要过滤掉
部署时的 Metastore 升级 需要仔细测试以确保引入继承不会破坏现有的元存储实例 没什么特别的,只是添加一个新属性

2009 年 12 月 30 日更新 :基于设计审查会议,我们将采用扁平模型。 Prasad 指出,将来,对于物化视图,我们可能还需要在分区级别上跟踪视图定义,因此,当我们更改视图定义时,如果新的物化分区不存在,则不必丢弃现有的物化分区。查看结果可以从旧的中得出。因此,将视图定义添加为 StorageDescriptor 的新属性可能是有道理的(因为在表级别和分区级别都已经存在)。

2010 年 1 月 20 日更新 :与 Prasad 进一步讨论后,我们决定将视图定义放到表对象上;有关详细信息,请参见HIVE-972中的讨论。此外,对于HIVE-1068,我们添加了一个属性来存储每个表 Descriptors 的类型(视图,托管表,外部表)。

Dependency Tracking

必须跟踪从视图到其在 metastore 中引用的对象的依赖关系:

  • 表:这是强制性的,如果我们希望 DROP TABLE 能够正确地将 CASCADE/RESTRICT 引用到引用视图中

  • 其他视图:与表相同

  • 列:这是可选的(用于沿袭检查,但对于实现 SQL 功能不是必需的)

  • 临时函数:除非我们也希望使用临时视图的概念(或者在清除易失性函数注册表时引用视图变为无效,否则我们应该在视图创建时禁止使用这些函数)

  • 还有其他物体吗? (例如,udt 作为http://issues.apache.org/jira/browse/HIVE-779的一部分出现)

(请注意,MySQL 实际上并没有实现 CASCADE/RESTRICT:它只是忽略了关键字并无条件地删除了表,从而使视图悬空了.)

Metastore 对象 ID 可以用于依赖关系建模,以避免在重命名对象时需要更新依赖关系记录。但是,我们需要确定哪些对象可以参与依赖关系。例如,如果我们仅将其限制为表和视图(并且假设我们不引入视图的继承),则可以使用以下模型,在该模型中,相关性将作为(供应商,Consumer)表对进行跟踪。 (在此模型中,TableDependency 类用作实现供应商和 Consumer 之间多对多关系的交集表)。

但是,如果以后我们要引入持久性函数或跟踪列依赖性,则此模型将不够用,并且我们可能需要引入具有 DependencyParticipantBase Class 的继承,所有这些继承类都派生表,列,函数等。 (再次,需要验证 JDO 继承实际上将支持我们想要的功能.)

2009 年 12 月 30 日更新 :基于一次设计审查会议,我们将从最低限度的 MySQL 方法开始(不对依赖项跟踪提供任何元存储支持),然后在时间允许的情况下添加依赖项分析和存储,其次是 CASCADE 支持。参见 HIVE-1073 和 HIVE-1074.

Dependency Invalidation

在视图下修改对象时会发生什么?例如,假设视图引用了表的列,然后使用 ALTER TABLE 删除或替换该列。请注意,如果列的数据类型发生更改,则视图定义可能仍然有意义,但是可能需要更新视图的架构以进行匹配。这是两个可能的选项:

  • 严格 :防止以任何方式使视图无效或更改视图的操作(并可选地提供 CASCADE 标志,请求自动删除此类视图)。这是 SQL:200n 采取的方法。

  • Lenient :允许进行更新(并可能警告用户影响),从而可能使视图处于无效状态。以后,当引用无效的视图定义时,引发引用查询的验证异常。这是 MySQL 采取的方法。对于数据类型更改,在重新创建这些视图之前,已经存储在 metastore 中用于引用视图的派生列数据类型将变得过时。

请注意,除了修改表外,其他操作(例如 CREATE 或 REPLACE VIEW)也有类似的问题(因为视图可以引用其他视图)。宽松的方法为架构可能是动态的外部表的相关问题提供了一个合理的解决方案(不确定我们当前是否支持)。

2009 年 12 月 30 日更新 :基于一次设计审查会议,我们将从宽大的方法开始,不支持在 metastore 中将对象标记为无效,然后在时间允许的情况下采取严格的支持措施,甚至可能 Metastore 支持跟踪对象的有效性。参见 HIVE-1077.

View Modification

在 SQL:200n 中,没有标准的方法来更新视图定义。 MySQL 同时支持

  • 创建或替换视图 v 作为 new-view-def-select

  • ALTER VIEW v AS new-view-def-select

请注意,支持视图修改需要检测循环视图定义,这应该是无效的。在视图修改还是参考时,是否执行此检测取决于上述依赖关系失效的严格方法还是宽松方法。

2009 年 12 月 30 日更新 :基于一次设计审查会议,我们将从 Oracle 风格的 ALTER VIEW v RECOMPILE 开始,该模型可用于重新验证视图定义以及重新扩展视图定义。子句(如 select *)的原始定义。然后,如果时间允许,我们将 continue 提供 CREATE 或 REPLACE VIEW 支持。 (由于我们使用宽大的失效模型,因此后者不那么重要,这使得 DROP 和 re-CREATE 成为可能,而不必处理下游依赖性.)请参阅 HIVE-1077 和 HIVE-1078.

快速路径执行

对于 select * from t ,hive 支持快速路径执行(跳过 Map/Reduce)。对于从 v 进行 select *的工作也很重要吗?

2009 年 12 月 30 日更新 :基于 JIRA 的反馈,我们将其取决于要使快速路径适用于基础滤波器和预测。

2010 年 12 月 6 日更新 :Hive 的新“自动本地模式”功能解决了这一问题。

视图定义中的 ORDER BY 和 LIMIT

SQL:200n 禁止在视图定义中使用 ORDER BY,因为该视图应该是虚拟(无序)表,而不是查询别名。但是,许多 DBMS 忽略了此规则。例如,MySQL 允许使用 ORDER BY,但在查询中被 ORDER BY 取代的情况下,则将其忽略。我们应该防止 ORDER BY 吗?此问题也适用于 LIMIT 子句。

2009 年 12 月 30 日更新 :基于 JIRA 的反馈,ORDER BY 作为实现物化视图的前瞻性非常重要。 LIMIT 可能没那么重要,但是为了保持一致性,我们可能也应该支持它。

基本分区依赖性

2009 年 12 月 30 日更新 :Prasad 指出,即使不支持实例化视图,也可能有必要向用户提供有关视图与基础表分区之间的数据依赖关系的元数据,以便用户避免在窗口期间看到不一致的结果并非所有分区都已用最新数据刷新。一种选择是尝试自动导出此信息(在无法充分依赖分析的情况下,使用过度保守的猜测);另一个是允许视图创建者以某种方式声明依赖关系规则,作为视图定义的一部分。在一次设计审查会议的基础上,一旦实现了依赖性跟踪,我们可能会采用自动分析方法。分析将按需执行,可能是描述视图或针对视图提交查询作业的一部分。在实现此功能之前,用户可能可以通过经验沿袭工具或通过 view-> table 依赖项跟踪元数据进行自己的分析。参见 HIVE-1079.

2011 年 2 月 1 日更新 :有关此内容的最新信息,请参见PartitionedViews

Metastore Upgrades

由于我们将新列添加到 metastore 模式中的 TBLS 表中,因此将需要升级现有的 metastore 部署。可以通过两种方式发生这种情况。

自动 ALTER TABLE

如果在 Hive 配置文件中设置了以下属性,JDO 将注意到持久性架构与模型之间的差异,并自动更改表:

<property>
<name>datanucleus.autoCreateSchema</name>
<value>true</value>
</property>

明确的 ALTER TABLE

但是,如果datanucleus.autoCreateSchema属性设置为false,则必须显式执行 ALTER 语句。 (Management 员可能已出于安全考虑在生产配置中设置了此属性.)

在这种情况下,对 metastore 数据库执行如下脚本:

ALTER TABLE TBLS ADD COLUMN VIEW_ORIGINAL_TEXT MEDIUMTEXT;
ALTER TABLE TBLS ADD COLUMN VIEW_EXPANDED_TEXT MEDIUMTEXT;
ALTER TABLE TBLS ADD COLUMN TBL_TYPE VARCHAR(128);

此处的语法适用于 MySQL,因此您可能需要对其进行调整(尤其是对于 CLOB 数据类型)。

请注意,在升级 Hive 之前执行此脚本并 continue 操作是安全的。旧的 Hive 版本只会忽略/无效它无法识别的列。

现有行更新

更改表后,新列将包含描述先前创建的表的现有行的 NULL 值。这对于 VIEW_ORIGINAL_TEXT 和 VIEW_EXPANDED_TEXT 是正确的(因为以前不存在视图),但是对于HIVE-1068引入的 TBL_TYPE 列则不正确。新的 Hive 代码能够处理此问题(在检索 Descriptors 时自动为新字段填充正确的值),但是它不能“固定”存储的行。如果将来使用其他工具直接从 metastore 数据库中检索信息而不是访问 metastore API,则可能会出现问题。

更改表后,以下脚本可用于修复现有行。它应在所有直接访问 metastore 数据库的 Hive 实例升级之后运行(否则,新的 null 值可能会渗入并永久保留)。为了安全起见,如果已经执行了 CREATE VIEW 语句,则它具有视图感知功能,这意味着可以在升级后的任何时间重新运行它。

UPDATE TBLS SET TBL_TYPE='MANAGED_TABLE'
WHERE VIEW_ORIGINAL_TEXT IS NULL
AND NOT EXISTS(
    SELECT * FROM TABLE_PARAMS 
    WHERE TABLE_PARAMS.TBL_ID=TBLS.TBL_ID
    AND PARAM_KEY='EXTERNAL'
    AND PARAM_VALUE='TRUE'
);
UPDATE TBLS SET TBL_TYPE='EXTERNAL_TABLE'
WHERE EXISTS(
    SELECT * FROM TABLE_PARAMS 
    WHERE TABLE_PARAMS.TBL_ID=TBLS.TBL_ID
    AND PARAM_KEY='EXTERNAL'
    AND PARAM_VALUE='TRUE'
);

对于 MySQL,请注意,由于这些是全表更新,因此需要禁用“安全更新”功能。