apache-hive / 3.1.1 / reference / JDBC_Storage_Handler.html

JDBC 存储处理程序

Syntax

JdbcStorageHandler 支持从 Hive 中的 jdbc 数据源读取。当前不支持写入 jdbc 数据源。要使用 JdbcStorageHandler,您需要使用 JdbcStorageHandler 创建一个外部表。这是一个简单的示例:

CREATE EXTERNAL TABLE student_jdbc
(
  name string,
  age int,
  gpa double
)
STORED BY 'org.apache.hive.storage.jdbc.JdbcStorageHandler'
TBLPROPERTIES (
    "hive.sql.database.type" = "MYSQL",
    "hive.sql.jdbc.driver" = "com.mysql.jdbc.Driver",
    "hive.sql.jdbc.url" = "jdbc:mysql://localhost/sample",
    "hive.sql.dbcp.username" = "hive",
    "hive.sql.dbcp.password" = "hive",
    "hive.sql.table" = "STUDENT",
    "hive.sql.dbcp.maxActive" = "1"
);

您还可以使用 alter table 语句来更改 jdbc 外部表的表属性,就像其他 nonlocalHive 表一样:

ALTER TABLE student_jdbc SET TBLPROPERTIES ("hive.sql.dbcp.password" = "passwd");

Table Properties

在 create table 语句中,需要指定以下表属性:

  • hive.sql.database.type:MYSQL,POSTGRES,ORACLE,DERBY,DB2

  • hive.sql.jdbc.url:jdbc 连接字符串

  • hive.sql.jdbc.driver:jdbc 驱动程序类

  • hive.sql.dbcp.username:jdbc 用户名

  • hive.sql.dbcp.password:明文形式的 jdbc 密码,强烈建议不要使用此参数。推荐的方法是将其存储在密钥库中。有关详细信息,请参见“保护密码”部分。

  • hive.sql.table/hive.sql.query:您将需要指定“ hive.sql.table”或“ hive.sql.query”以告诉如何从 jdbc 数据库获取数据。 “ hive.sql.table”表示单个表,“ hive.sql.query”表示任意 sql 查询。

除了上述必需属性之外,您还可以指定可选参数来调整连接详细信息和性能:

  • hive.sql.catalog:jdbc 目录名称(仅在指定“ hive.sql.table”时有效)

  • hive.sql.schema:jdbc 模式名称(仅在指定“ hive.sql.table”时有效)

  • hive.sql.jdbc.fetch.size:要批量提取的行数

  • hive.sql.dbcp.xxx:所有 dbcp 参数将传递给 commons-dbcp。有关参数的定义,请参见https://commons.apache.org/proper/commons-dbcp/configuration.html。例如,如果您在表属性中指定 hive.sql.dbcp.maxActive = 1,则 Hive 会将 maxActive = 1 传递给 commons-dbcp

支持的数据类型

Hive JdbcStorageHandler 表的列数据类型可以是:

  • 数值数据类型:字节,短整数,整数,长整数,浮点数,双精度

  • 小数位数和精度

  • 字符串日期类型:string,char,varchar

  • Date

  • Timestamp

注意复杂的数据类型:不支持 struct,map,array

Column/Type Mapping

hive.sql.table/hive.sql.query 使用模式定义表格数据。模式定义必须与表模式定义相同。例如,以下创建表语句将失败:

CREATE EXTERNAL TABLE student_jdbc
(
  name string,
  age int,
  gpa double
)
STORED BY 'org.apache.hive.storage.jdbc.JdbcStorageHandler'
TBLPROPERTIES (
    . . . . . .
    "hive.sql.query" = "SELECT name, age, gpa, gender FROM STUDENT",
);

但是,hive.sql.table/hive.sql.query 架构的列名和列类型可能与表架构不同。在这种情况下,数据库列按位置 Map 到配置单元列。如果数据类型不同,Hive 将尝试根据 Hive 表模式进行转换。例如:

CREATE EXTERNAL TABLE student_jdbc
(
  sname string,
  age int,
  effective_gpa decimal(4,3)
)
STORED BY 'org.apache.hive.storage.jdbc.JdbcStorageHandler'
TBLPROPERTIES (
    . . . . . .
    "hive.sql.query" = "SELECT name, age, gpa FROM STUDENT",
);

Hive 将尝试将带下划线的表 STUDENT 的双“ gpa”转换为“ student_jdbc”表的 valid_gpa 字段的十进制(4,3)。如果无法进行转换,则 Hive 将为该字段生成 null。

Auto Shipping

如果在查询中使用 JdbcStorageHandler,则 JdbcStorageHandler 会将所需的 jar 自动发送到 MR/Tez/LLAP 后端。用户不需要手动添加 jar。如果 JdbcStorageHandler 在 Classpath(包括 mysql,postgres,oracle 和 mssql)中检测到任何 jdbc 驱动程序 jar,它还将把所需的 jdbc 驱动程序 jar 发送到后端。但是,仍然需要用户将 jdbc 驱动程序 jar 复制到 hiveClasspath(通常是 hive 中的 lib 目录)。

Securing Password

在大多数情况下,我们不想在表属性“ hive.sql.dbcp.password”中以明文形式存储 jdbc 密码。相反,用户可以使用以下命令将密码存储在 HDFS 上的 Java 密钥存储文件中:

hadoop credential create host1.password -provider jceks://hdfs/user/foo/test.jceks -v passwd1
hadoop credential create host2.password -provider jceks://hdfs/user/foo/test.jceks -v passwd2

这将在hdfs://user/foo/test.jceks上创建一个密钥库文件,该文件包含两个密钥:host1.password 和 host2.password。在 Hive 中创建表时,您需要在 create table 语句中指定“ hive.sql.dbcp.password.keystore”和“ hive.sql.dbcp.password.key”,而不是“ hive.sql.dbcp.password”:

CREATE EXTERNAL TABLE student_jdbc
(
  name string,
  age int,
  gpa double
)
STORED BY 'org.apache.hive.storage.jdbc.JdbcStorageHandler'
TBLPROPERTIES (
    . . . . . .
    "hive.sql.dbcp.password.keystore" = "jceks://hdfs/user/foo/test.jceks",
    "hive.sql.dbcp.password.key" = "host1.password",
    . . . . . .
);

您只需要授权目标用户使用授权程序(例如 Ranger)读取此文件,就可以保护密钥库文件。 Hive 将检查密钥库文件的权限,以确保用户在创建/更改表时具有对其的读取权限。

Partitioning

Hive 能够拆分 jdbc 数据源并并行处理每个拆分。用户可以使用以下表格属性来决定是否拆分以及拆分为多少拆分:

  • hive.sql.numPartitions:要为数据源生成多少拆分,如果没有拆分则为 1

  • hive.sql.partitionColumn:要拆分的列。如果指定了此选项,则 Hive 会将列拆分为从 hive.sql.lowerBound 到 hive.sql.upperBound 相等间隔的 hive.sql.numPartitions。如果未定义 partitionColumn 但 numPartitions> 1,则 Hive 将使用 offset 拆分数据源。但是,对于某些数据库,偏移量并不总是可靠的。如果要拆分数据源,强烈建议定义一个 partitionColumn。 partitionColumn 必须存在于架构“ hive.sql.table” /“ hive.sql.query”产生的模式中。

  • hive.sql.lowerBound/hive.sql.upperBound:用于计算间隔的 partitionColumn 的上下边界。这两个属性都是可选的。如果未定义,Hive 将针对数据源进行 MIN/MAX 查询,以获取下限/上限。请注意,hive.sql.lowerBound 和 hive.sql.upperBound 都不能为 null。第一个和最后一个分割是开放式的。并且该列的所有空值都将进入第一个拆分。

For example:

TBLPROPERTIES (
    . . . . . .
    "hive.sql.table" = "DEMO",
    "hive.sql.partitionColumn" = "num",
    "hive.sql.numPartitions" = "3",
    "hive.sql.lowerBound" = "1",
    "hive.sql.upperBound" = "10",
    . . . . . .
);

该表将创建 3 个拆分:num<4 or num is null, 4<=num<7, num> = 7

TBLPROPERTIES (
    . . . . . .
    "hive.sql.query" = "SELECT name, age, gpa/5.0*100 AS percentage FROM STUDENT",
    "hive.sql.partitionColumn" = "percentage",
    "hive.sql.numPartitions" = "4",
    . . . . . .
);

Hive 将执行 jdbc 查询以获取查询的百分比列的 MIN/MAX,即 60、100.然后表格将创建 4 个拆分:(,70),[70,80),[80,90), [90,)。第一个拆分还包括空值。

要查看 JdbcStorageHandler 生成的拆分,请在 hiveserver2 日志或 Tez AM 日志中查找以下消息:

jdbc.JdbcInputFormat: Num input splits created 4
jdbc.JdbcInputFormat: split:interval:ikey[,70)
jdbc.JdbcInputFormat: split:interval:ikey[70,80)
jdbc.JdbcInputFormat: split:interval:ikey[80,90)
jdbc.JdbcInputFormat: split:interval:ikey[90,)

Computation Pushdown

Hive 将积极地将计算下推到 jdbc 表,因此我们可以充分利用 jdbc 数据源的本机容量。

例如,如果我们有另一个表表决器_jdbc:

EATE EXTERNAL TABLE voter_jdbc
(
  name string,
  age int,
  registration string,
  contribution decimal(10,2)
)
STORED BY 'org.apache.hive.storage.jdbc.JdbcStorageHandler'
TBLPROPERTIES (
    "hive.sql.database.type" = "MYSQL",
    "hive.sql.jdbc.driver" = "com.mysql.jdbc.Driver",
    "hive.sql.jdbc.url" = "jdbc:mysql://localhost/sample",
    "hive.sql.dbcp.username" = "hive",
    "hive.sql.dbcp.password" = "hive",
    "hive.sql.table" = "VOTER"
);

然后,以下联接操作将下推至 mysql:

select * from student_jdbc join voter_jdbc on student_jdbc.name=voter_jdbc.name;

这可以通过解释来体现:

explain select * from student_jdbc join voter_jdbc on student_jdbc.name=voter_jdbc.name;
        . . . . . .
        TableScan
          alias: student_jdbc
          properties:
            hive.sql.query SELECT `t`.`name`, `t`.`age`, `t`.`gpa`, `t0`.`name` AS `name0`, `t0`.`age` AS `age0`, `t0`.`registration`, `t0`.`contribution`
FROM (SELECT *
FROM `STUDENT`
WHERE `name` IS NOT NULL) AS `t`
INNER JOIN (SELECT *
FROM `VOTER`
WHERE `name` IS NOT NULL) AS `t0` ON `t`.`name` = `t0`.`name`
        . . . . . .

仅当“ hive.sql.table”定义了 jdbc 表时,才会进行计算下推。 Hive 将使用“ hive.sql.query”属性重写数据源,并在表顶部进行更多计算。在上面的示例中,mysql 将运行查询并检索联接结果,而不是获取两个表并在 Hive 中进行联接。

运算符可以下推,包括过滤器,转换,联接,联合,聚合和排序。

派生的 mysql 查询可能非常复杂,在许多情况下,我们不想拆分数据源,因此每次拆分都会多次运行复杂查询。因此,如果计算更多,而不仅仅是过滤和变换,即使“ hive.sql.numPartitions”大于 1,Hive 也不会拆分查询结果。