Indexes

自 3.0 起删除索引

还有一些替代选项可能与索引相似:

  • 具有自动重写的实例化视图可以产生非常相似的结果。 Hive 2.3.0添加了对实例化视图的支持。

  • 使用柱状文件格式(ParquetORC)–他们可以进行选择性扫描;他们甚至可能会跳过整个文件/块。

Note

索引已在 3.0 版(HIVE-18448)中被“删除”。

Introduction

本文解释了为 Hive(HIVE-417)添加索引支持的建议设计。索引是一种标准的数据库技术,但是有许多可能的变化。我们采用的方法不是尝试提供“一刀切”的索引实现,而是采用可插拔的方式定义索引(与StorageHandlers相关),并提供一个具体的索引实现作为参考,从而为随着时间的流逝,贡献者可以插入其他索引方案。直到 Hive 0.7 才提供索引支持。

Scope

仅支持单表索引。一旦 Hive 支持了其他视图(例如联接索引),则可以更恰当地将其表示为实例化视图。

本文档当前仅涵盖索引的创建和维护。后续文章将说明如何使用索引优化查询(构建在FilterPushdownDev上)。

CREATE INDEX

CREATE INDEX index_name
ON TABLE base_table_name (col_name, ...)
AS 'index.handler.class.name'
[WITH DEFERRED REBUILD]
[IDXPROPERTIES (property_name=property_value, ...)]
[IN TABLE index_table_name]
[PARTITIONED BY (col_name, ...)]
[
   [ ROW FORMAT ...] STORED AS ...
   | STORED BY ...
]
[LOCATION hdfs_path]
[TBLPROPERTIES (...)]
[COMMENT "index comment"]

有关 ROW FORMAT 等各种子句的详细信息,请参见Create Table

默认情况下,索引分区与基表的分区匹配。 PARTITIONED BY 子句可用于指定表分区列的子集(此列列表可能为空,以指示索引跨越表的所有分区)。例如,即使索引仅按日期进行分区(每个索引分区跨越所有区域),也可以按日期区域对表进行分区。

无法在视图上创建索引。我们(最终)将在非本机表上支持它们(如果相应的存储处理程序指示它支持它们)。

索引处理程序可能要求被索引的基表具有特定格式。

问题:我们应该允许 EXTERNAL 表上的索引吗?删除表后,这对于隐式 DROP 意味着什么?是否存在 EXTERNAL 索引的概念?

如果索引处理程序以表格形式存储其表示形式,则可以使用 index_table_name 来控制为此目的自动创建的“索引表”的名称。可以使用 STORED AS(例如 RCFILE 或 SEQUENCFILE)或 STORED BY(例如将索引表存储在 nonlocal 表(例如 HBase)中)来控制索引表的存储格式,尽管某些索引处理程序可能需要使用特定的存储格式。并非所有的索引处理程序都以表格形式存储它们的表示形式。一些可能使用非表文件,而另一些可能使用完全在 Hive 外部维护的结构(例如,持久键/值存储)。

Metastore Model

下图显示了具有索引支持的新 Metastore 模式:

http://issues.apache.org/jira/secure/attachment/12449601/idx2.png

Metastore 模式中的新 IDXS 表每个创建的索引包含一个条目。它与 TBLS 表有两个关系:

  • ORIG_TBL_ID 是强制性外键,它引用包含要构建索引的数据的基表的 ID。

  • IDX_TBL_ID 是一个可选的外键,它引用包含索引表示形式的表的 ID。它是可选的,因为并非所有索引实现都使用表进行存储。对于确实使用表进行存储的索引,隐式创建的表会将其 TBL_TYPE 设置为 INDEX_TABLE。

因此,给定以下 DDL:

CREATE TABLE t(i int, j int);
CREATE INDEX x ON TABLE t(j)
AS 'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler';

元存储中的 TBLS 表将具有两个条目:

  • 一个用于基本表 t

  • 一个用于索引表,自动命名为default+t_x+

在 x 的 IDXS 条目中,ORIG_TBL_ID 将引用 x 的 TBL_ID,IDX_TBL_ID 将引用default+t_x+的 TBL_ID。

为了避免生成名称,可以改为提供用户指定的名称,例如 t_x:

CREATE INDEX x ON TABLE t(j)
AS 'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler'
IN TABLE t_x;

请注意,索引名称由包含的基表(如分区)限定,因此可以在两个不同的表中使用相同的索引名称。但是,索引表的名称与所有其他表和视图在同一命名空间中,因此它们在同一数据库中必须唯一。

索引具有一个存储 Descriptors,该 Descriptors 包含索引所覆盖的原始表中的列的子集。如果索引表示形式存储在表中,则索引自身的存储 Descriptors 中的其他大多数字段(例如 LOCATION)将是无关紧要的。

TBD:

  • 将 IDX_TYPE 更改为 IDX_HANDLER

  • LAST_ACCESS_TIME 是什么意思?上一次优化程序使用此索引?

  • 需要 LAST_REBUILD_TIME?我们如何在分区级别跟踪它?它应该在 metastore 中(而不仅仅是 HDFS)

  • 在索引分区是基表分区的子集的情况下,我们需要一种在元存储中对此建模的方法

Metastore Upgrades

这是 MySQL Metastore 升级声明。

DROP TABLE IF EXISTS {{IDXS}};
CREATE TABLE {{IDXS}} (
  {{INDEX_ID}} bigint(20) NOT NULL,
  {{CREATE_TIME}} int(11) NOT NULL,
  {{DEFERRED_REBUILD}} bit(1) NOT NULL,
  {{INDEX_HANDLER_CLASS}} varchar(256) DEFAULT NULL,
  {{INDEX_NAME}} varchar(128) DEFAULT NULL,
  {{INDEX_TBL_ID}} bigint(20) DEFAULT NULL,
  {{LAST_ACCESS_TIME}} int(11) NOT NULL,
  {{ORIG_TBL_ID}} bigint(20) DEFAULT NULL,
  {{SD_ID}} bigint(20) DEFAULT NULL,
  PRIMARY KEY ({{INDEX_ID}}),
  UNIQUE KEY {{UNIQUEINDEX}} ({{INDEX_NAME}},{{ORIG_TBL_ID}}),
  KEY {{IDXS_FK1}} ({{SD_ID}}),
  KEY {{IDXS_FK2}} ({{INDEX_TBL_ID}}),
  KEY {{IDXS_FK3}} ({{ORIG_TBL_ID}}),
  CONSTRAINT {{IDXS_FK3}} FOREIGN KEY ({{ORIG_TBL_ID}}) REFERENCES {{TBLS}} ({{TBL_ID}}),
  CONSTRAINT {{IDXS_FK1}} FOREIGN KEY ({{SD_ID}}) REFERENCES {{SDS}} ({{SD_ID}}),
  CONSTRAINT {{IDXS_FK2}} FOREIGN KEY ({{INDEX_TBL_ID}}) REFERENCES {{TBLS}} ({{TBL_ID}})
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Table structure for table {{INDEX_PARAMS}}
--

DROP TABLE IF EXISTS {{INDEX_PARAMS}};
CREATE TABLE {{INDEX_PARAMS}} (
  {{INDEX_ID}} bigint(20) NOT NULL,
  {{PARAM_KEY}} varchar(256) NOT NULL,
  {{PARAM_VALUE}} varchar(767) DEFAULT NULL,
  PRIMARY KEY ({{INDEX_ID}},{{PARAM_KEY}}),
  CONSTRAINT {{INDEX_PARAMS_FK1}} FOREIGN KEY ({{INDEX_ID}}) REFERENCES {{IDXS}} ({{INDEX_ID}})
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

REBUILD

ALTER INDEX index_name ON table_name [PARTITION (...)] REBUILD

有关 PARTITION 子句的语法,请参见LanguageManual DDL#Add_Partitions

如果在 CREATE INDEX 上指定了 WITH DEFERRED REBUILD,则新创建的索引最初为空(无论表是否包含任何数据)。 ALTER INDEX ... REBUILD 命令可用于为所有分区或单个分区构建索引结构。

如果基表中的数据发生更改,则必须使用 REBUILD 命令使索引更新。这是一个原子操作,因此如果该表先前已被索引,并且重建失败,则旧索引保持不变。

DROP INDEX

DROP INDEX index_name ON table_name

可以使用 DROP INDEX 随时删除索引。这也将级联到索引表(如果存在)。

尝试直接使用 DROP TABLE 删除索引表将失败。

当删除索引基表时,DROP 隐式级联到所有索引(及其对应的索引表,如果有的话)。

当索引基表删除其分区之一时,这将隐式级联以从所有索引中删除相应的分区。

问题:如果索引分区粒度与表分区粒度不同,该怎么办?可能只是忽略删除操作,而让用户使用新的 ALTER INDEX DROP PARTITION 语句进行手动清理。

Plugin Interface

索引处理程序具有以下主要职责:

  • 在 CREATE INDEX 期间,验证基本表的格式,然后生成索引表的结构(如果有),并将所有其他信息填充到索引的存储 Descriptors 中

  • 在 REBUILD 期间,生成一个计划,用于读取基表的数据并写入索引存储和/或索引表

  • 在 DROP 期间,删除任何特定于索引的存储(Hive 自动删除索引表)

  • 在查询期间,参与优化以便将诸如过滤器之类的运算符转换为索引访问计划(此部分目前不在范围内)

下面定义了相应的 Java 接口以及处理程序应扩展的伴随抽象 Base Class。

package org.apache.hadoop.hive.ql.metadata;

import java.util.List;

import org.apache.hadoop.conf.Configurable;

import org.apache.hadoop.hive.ql.plan.api.Task;

/**
 * HiveIndexHandler defines a pluggable interface for adding new
 * index handlers to Hive.
 */
public interface HiveIndexHandler extends Configurable
{
  /**
 * Determines whether this handler implements indexes by creating
 * an index table.
   *
 * @return true if index creation implies creation of an index table in Hive;
 * false if the index representation is not stored in a Hive table
   */
  boolean usesIndexTable();

  /**
 * Requests that the handler validate an index definition and
 * fill in additional information about its stored representation.
 *
 * @param baseTable the definition of the table being indexed
   *
 * @param index the definition of the index being created
   *
 * @param indexTable a partial definition of the index table to be used for
 * storing the index representation, or null if usesIndexTable() returns
 * false; the handler can augment the index's storage descriptor
 * (e.g. with information about input/output format)
 * and/or the index table's definition (typically with additional
 * columns containing the index representation, e.g. pointers into HDFS)
   *
 * @throw HiveException if the index definition is invalid with
 * respect to either the base table or the supplied index table definition
   */
  void analyzeIndexDefinition(
    org.apache.hadoop.hive.metastore.api.Table baseTable,
    org.apache.hadoop.hive.metastore.api.Index index,
    org.apache.hadoop.hive.metastore.api.Table indexTable)
      throws HiveException;

  /**
 * Requests that the handler generate a plan for building the index;
 * the plan should read the base table and write out the index representation.
   *
 * @param baseTable the definition of the table being indexed
   *
 * @param index the definition of the index
   *
 * @param partitions a list of specific partitions of the base
 * table for which the index should be built, or null if
 * an index for the entire table should be rebuilt
   *
 * @param indexTable the definition of the index table, or
 * null if usesIndexTable() returns null
   *
 * @return list of tasks to be executed in parallel for building
 * the index
   *
 * @throw HiveException if plan generation fails
   */
  List<Task<?>> generateIndexBuildTaskList(
    org.apache.hadoop.hive.metastore.api.Table baseTable,
    org.apache.hadoop.hive.metastore.api.Index index,
    List<org.apache.hadoop.hive.metastore.api.Partition> partitions,
    org.apache.hadoop.hive.metastore.api.Table indexTable)
      throws HiveException;
}

/**
 * Abstract base class for index handlers.  This is provided as insulation
 * so that as HiveIndexHandler evolves, default implementations of new
 * methods can be added here in order to avoid breaking existing
 * plugin implementations.
 */
public abstract class AbstractIndexHandler implements HiveIndexHandler
{
}

对于 CREATE INDEX,Hive 首先在处理程序上调用 usingIndexTable()以确定是否将创建索引表。如果返回 false,则如果用户为索引指定了任何表存储选项,则该语句将立即失败。但是,如果 usesIndexTable()返回 true,则 Hive 会根据索引定义(例如覆盖的列)以及用户提供的任何表存储选项,为索引表创建部分表定义。接下来,Hive 调用 analyticsIndexDefinition(为 indexTable 参数传递 null 或部分索引表定义)。处理程序通过验证定义进行响应(如果检测到任何不受支持的组合,则引发异常),然后将有关 index 和 indexTable 参数的附加信息填充为输出。然后,Hive 将这些结果存储在 metastore 中。

TBD:我们将添加删除索引时调用处理程序的方法(例如,为将索引表示形式存储在外部系统(例如 HBase)中的处理程序提供清理机会)

Reference Implementation

参考实现将创建所谓的“紧凑”索引。这意味着,它不存储每次出现特定值的 HDFS 位置,而是仅存储包含该值的 HDFS 块的地址。在值通常在附近的行中多次出现的情况下,此方法针对点查找进行了优化。索引大小保持较小,因为块少于行。权衡是在查询过程中需要额外的工作才能从索引块中滤除其他行。

紧凑索引存储在索引表中。索引表列由基础表中的索引列组成,其后是_bucketname 字符串列(指示包含已索引块的文件的名称),其后是_offsets array<string>列(指示相应文件中的块偏移)。索引表按排序方式存储在索引列上(但不存储在生成的列上)。

参考实现可以插入

ADD JAR /path/to/hive_idx-compact.jar;
CREATE INDEX ...
AS 'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler';

TBD:用于构建索引的算法

TBD:搜索索引的机制

TBD:在基表上验证(可以是任何托管表吗?)

TBD:验证索引表格式(可以是任何托管表格式吗?)

TBD

  • SHOW/DESCRIBE INDEX(HIVE-1497)的规格

  • 更改索引删除分区吗?

  • ALTER INDEX SET IDXPROPERTIES,更改表格式等

  • 当表或分区的结构在被索引后发生变化时会发生什么

  • 未指定 WITH DEFERRED REBUILD 时将自动索引作为 INSERT 的一部分

  • 防止在索引表上创建索引?

  • Metastore 升级脚本

  • 索引表的统计信息收集

  • 与新的 Hive 并发控制功能相交(我们对各种索引操作采取什么锁?)

当前状态(JIRA)

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

Loading...

Refresh