sparksql 读取hive表子目录问题排查

版本:spark3.0.2

1.1. 问题现象:

    业务使用hive union all产生的数据目录如下,sparksql无法读取该子目录下的数据。select * from table 这种简单的sql也无法读取。

一些博客建议设置下面两个参数,spark.hive.mapred.supports.subdirectories=true和spark.hadoop.mapreduce.input.fileinputformat.input.dir.recursive=true, 设置后sparksql依然无法读取,问题依然存在。

1.2. 问题分析:

      查看物理执行计划,叶子节点为FileSourceScanExec,该执行计划是spark内置的读取文件的SparkPlan,根本就不会使用spark.hive.mapred.supports.subdirectories和spark.hadoop.mapreduce.input.fileinputformat.input.dir.recursive,这两个配置。

      那么在FileSourceScanExec的逻辑中,是否能够处理这种子目录的情况呢。答案是目前FileSourceScanExec无法递归处理子目录:

      FileSourceScanExec的selectedPartitions的属性保存了需要的读取的分区文件,这些文件信息来自PartitioningAwareFileIndex的listFiles方法。leafDirToChildrenFiles这个map中保存的是子目录和数据文件的映射,而path为分区路径,所以这里get返回为空。

         既然FileSourceScanExec无法处理子目录,那有没有其他SparkPlan能够处理呢?

         spark读取hive表时,hive表在logicPlan中其实最开始是HiveTableRelation,然后HiveSessionStateBuilder的postHocResolutionRules中的RelationConversions规则,将HiveTableRelation转换成了LogicalRelation。这个LogicalRelation最终会转换成FileSourceScanExec。

        如果RelationConversions规则不生效,HiveTableRelation就不会转换成LogicalRelation,那最终HiveTableRelation对应的SparkPlan为HiveTableScanExec,在HiveTableScanExec中能够使用spark.hadoop.mapred.input.dir.recursive=true配置读取子目录。

        所以如何控制RelationConversions规则是否生效,见代码。 如果数据文件是orc,那么就设置spark.sql.hive.convertMetastoreOrc=false,如果数据文件是parquet,就设置spark.sql.hive.convertMetastoreParquet=false。 设置了参数后RelationConversions规则就不会生效。

1.3. 问题解决方法

如果是orc文件:
--conf spark.hadoop.mapred.input.dir.recursive=true --conf spark.sql.hive.convertMetastoreOrc=false
如果是parquet文件:
--conf spark.hadoop.mapred.input.dir.recursive=true --conf spark.sql.hive.convertMetastoreParquet=false

<think>好的,我现在需要解释Hive如何读取HDFS数据并写入数据库。首先,用户可能想知道Hive的基本工作原理,尤其是它与HDFS和数据库的交互。Hive通常用于大数据处理,用户可能是在进行数据迁移或ETL操作时需要了解这一过程。 首先,Hive读取HDFS数据,这部分涉及到Hive的定义,尤其是外部和内部的区别。用户可能已经将数据存储在HDFS上,需要通过Hive进行查询或处理。需要解释Hive如何通过元数据定位HDFS上的文件,以及执行查询时如何将这些数据加载到处理引擎中,比如MapReduce、Tez或Spark。 然后是写入数据库的部分。这里可能存在歧义,因为Hive本身通常将数据存储在HDFS中,而不是传统的关系型数据库。但用户可能指的是将Hive处理后的数据导出到外部数据库,如MySQL或PostgreSQL。或者,用户可能误以为Hive直接将数据写入某种数据库,需要澄清Hive通常与HDFS结合使用,而元数据存储在关系型数据库中。 需要分步骤说明整个流程:从Hive定义指向HDFS数据,执行查询处理,然后将结果写入另一个存储位置,可能是HDFS中的另一个目录,或者通过其他工具如Sqoop导出到外部数据库。需要提到Hive的INSERT语句,以及如何将结果导出到不同的存储系统。 同时,用户可能关心优化技巧,比如存储格式(ORC、Parquet)、分区、分桶,以及如何通过Hive配置提高读写效率。此外,权限问题、数据格式兼容性、网络配置等常见问题也需要提及。 需要检查是否有误解,比如Hive写入数据库的部分,可能需要确认用户是否指的是将数据从Hive导出到关系型数据库,这时候需要介绍Sqoop或其他数据传输工具的使用。如果是Hive内部将数据写入其管理的,那么需要说明Hive的数据存储位置在HDFS,元数据存储在Metastore数据库中(如MySQL)。 最后,需要结构清晰,逐步解释整个过程,确保用户理解Hive如何与HDFS交互,以及如何将处理后的数据输出到不同的目的地。如果有必要,提供示例代码和配置,帮助用户更好地实际操作。</think>以下是 Hive 从 HDFS 读取数据并写入数据库的 **核心流程、技术细节及实践方法** 的详细说明: --- ### **一、Hive 读取 HDFS 数据原理** #### **1. 元数据与物理存储分离** - **Hive 定义**:结构、字段类型等信息存储在 Metastore(如 MySQL)中。 - **实际数据存储**:数据文件(如 CSV、ORC)存放在 HDFS 指定路径下,例如:`hdfs://namenode:8020/user/hive/warehouse/sales`。 #### **2. 数据读取流程** 1. **SQL 解析**: Hive 将 SQL 语句(如 `SELECT * FROM sales`)解析为抽象语法树(AST)。 2. **编译为执行计划**: 将 AST 转换为 MapReduce/Tez/Spark 作业(例如:`TableScan` 操作对应读取 HDFS 文件)。 3. **HDFS 数据加载**: - Map Task 读取 HDFS 文件块(默认按 128MB 分片) - 根据定义的 `ROW FORMAT`(如 `DelimitedText`、`SerDe`)解析数据 ```sql CREATE TABLE sales ( id INT, product STRING ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' -- 指定字段分隔符 STORED AS TEXTFILE -- 文件存储格式 LOCATION '/user/hive/warehouse/sales'; -- HDFS 路径 ``` --- ### **二、Hive 写入数据库的两种场景** #### **场景 1:写入 Hive 托管(本质仍是 HDFS)** **操作流程**: 1. 创建目标: ```sql CREATE TABLE sales_summary ( product STRING, total_sales DOUBLE ) STORED AS ORC; -- 使用列式存储优化 ``` 2. 执行 INSERT 操作: ```sql INSERT INTO sales_summary SELECT product, SUM(amount) FROM sales GROUP BY product; -- 触发 MapReduce/Tez 作业 ``` **底层行为**: - 计算结果会写入 `hdfs://.../warehouse/sales_summary` 路径 - Metastore 更新元数据 #### **场景 2:写入外部数据库(如 MySQL)** **需配合 Sqoop 或 JDBC 实现**: 1. **Hive 计算结果暂存到 HDFS**: ```sql INSERT OVERWRITE DIRECTORY '/output/sales_summary' STORED AS TEXTFILE SELECT product, SUM(amount) FROM sales GROUP BY product; ``` 2. **使用 Sqoop 导出到 MySQL**: ```bash sqoop export \ --connect jdbc:mysql://mysql-server:3306/db \ --username root \ --password 123456 \ --table sales_summary \ --export-dir /output/sales_summary # 指向 HDFS 结果路径 ``` --- ### **三、关键配置优化** #### **1. 提升 HDFS 读取效率** - **使用列式存储**: ```sql CREATE TABLE sales_orc ( id INT, product STRING ) STORED AS ORC; -- 比 TEXTFILE 减少 60% I/O ``` - **分区裁剪**: ```sql CREATE TABLE sales_partitioned ( id INT, amount DOUBLE ) PARTITIONED BY (dt STRING); -- 按日期分区 SELECT * FROM sales_partitioned WHERE dt = '20231001'; -- 只读取对应分区数据 ``` #### **2. 写入优化** - **合并小文件**: ```sql SET hive.merge.mapredfiles=true; -- 合并 MapReduce 输出 SET hive.merge.size.per.task=256000000; -- 合并后文件大小 ``` - **批量插入**: ```sql SET hive.exec.dynamic.partition.mode=nonstrict; INSERT INTO TABLE sales_partitioned PARTITION (dt) VALUES (1001, 99.9, '20231001'), (1002, 149.9, '20231002'); -- 批量插入减少连接开销 ``` --- ### **四、实践案例:从 HDFS 到 MySQL 完整流程** #### **步骤 1:Hive 处理数据** ```sql -- 创建临时结果 CREATE TABLE tmp_result ( product STRING, total DOUBLE ) STORED AS ORC; -- 计算并插入结果 INSERT INTO tmp_result SELECT product, ROUND(SUM(amount), 2) FROM sales WHERE dt BETWEEN '20230101' AND '20231231' GROUP BY product; ``` #### **步骤 2:导出到 HDFS 中间目录** ```sql INSERT OVERWRITE DIRECTORY '/export/mysql_data' ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' SELECT * FROM tmp_result; ``` #### **步骤 3:Sqoop 导出到 MySQL** ```bash sqoop export \ --connect jdbc:mysql://10.10.10.10:3306/sales_db \ --username etl_user \ --password etl@123 \ --table product_summary \ --columns "product_name,total_sales" \ --export-dir /export/mysql_data \ --input-fields-terminated-by ',' # 匹配 Hive 输出格式 ``` --- ### **五、常见问题排查** #### **1. 数据格式不一致** - **现象**:Sqoop 导出时出现数据类型错误 - **解决**:确保 Hive 与 MySQL 字段类型匹配: - Hive `STRING` → MySQL `VARCHAR(255)` - Hive `DOUBLE` → MySQL `DECIMAL(12,2)` #### **2. 写入性能低下** - **优化方法**: ```sql SET hive.exec.parallel=true; -- 启用并行执行 SET hive.exec.parallel.thread.number=8; -- 并行线程数 ``` #### **3. 权限问题** - **HDFS 目录权限**: ```bash hadoop fs -chmod -R 777 /export/mysql_data # 确保 Sqoop 有访问权限 ``` --- ### **六、高级技巧** #### **1. 使用 Hive ACID 事务(Hive 3+)** ```sql CREATE TABLE acid_table ( id INT, name STRING ) STORED AS ORC TBLPROPERTIES ('transactional'='true'); -- 支持 UPDATE/DELETE INSERT INTO acid_table VALUES (1, 'Alice'); UPDATE acid_table SET name = 'Bob' WHERE id = 1; ``` #### **2. 通过 Hive Hook 监控数据流** ```xml <!-- hive-site.xml --> <property> <name>hive.exec.pre.hooks</name> <value>org.apache.hadoop.hive.ql.hooks.PreExecutePrinter</value> </property> <property> <name>hive.exec.post.hooks</name> <value>org.apache.hadoop.hive.ql.hooks.PostExecutePrinter</value> </property> ``` --- 如果需要更深入的 **Sqoop 调优参数** 或 **Hive 事务实现原理**,可以进一步说明具体需求!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值