HiveSQL优化点之多次insert单次扫描表

比如将一张表的数据多次查询出来装载到另外一张表中。如表table1是一个分区表,分区字段为dt,如果需要在表中查询多个特定的分区日期数据,并将数据装载到多个不同的表中或同一张表的不同分区中。一般会写成如下:

INSERT INTO table2 SELECT * FROM table1 WHERE dt ='2020-12-25';
INSERT INTO table3 SELECT * FROM table1 WHERE dt ='2020-12-26';
INSERT INTO table4 SELECT * FROM table1 WHERE dt ='2020-12-27';
INSERT INTO table5 SELECT * FROM table1 WHERE dt ='2020-12-28';
……

在以上查询中,Hive将扫描表多次,为了避免这种情况,我们可以使用下面的方式:

FROM table1
INSERT INTO table2 SELECT * WHERE dt ='2020-12-25'
INSERT INTO table3 SELECT * WHERE dt ='2020-12-26'
INSERT INTO table4 SELECT * WHERE dt ='2020-12-27'
INSERT INTO table5 SELECT * WHERE dt ='2020-12-28';

这样可以确保只对 table1表执行一次扫描,从而可以大大减少执行的时间和资源。

<think>嗯...用户这次明确要HiveSQL的性能优化技巧和最佳实践,看来是遇到实际性能问题了。上次对话中用户对SQL优化很关注,这次是专业领域的深度提问。用户可能正在处理大规模数据,遇到了执行效率瓶颈,需要可落地的解决方案。考虑到Hive常用于TB/PB级数据仓库,优化确实很关键。从技术角度看,Hive优化需要分层处理:首先是数据存储优化,这是基础;然后是查询编写技巧,这直接决定执行效率;最后是配置调优。用户可能更关注前两者,因为配置调优往往需要管理员权限。存储优化方面要重讲分区和分桶,这是Hive的核心理念。注意到用户习惯看代码示例,所有优化都需要配SQL范例说明。另外得提醒BloomFilter这类新特性,很多老用户可能还不知道。查询优化要强调数据倾斜处理,这是生产环境最常见痛。需要给出具体解决方案,比如用distributeby处理倾斜的实例。MapJoin优化也要重讲,小表关联的场景太普遍了。配置参数部分反而不用太多,列举最有价值的几个即可。最后务必加上执行计划分析技巧,授人以渔很重要。用户既然问最佳实践,应该是有长期优化需求的。对了,补充说明里要强调数据格式选择,ORC/Parquet的压缩比差异很关键,现在企业都在降本增效。所有建议必须标注适用场景,避免用户误用。</think> 好的,Hive SQL 性能优化是一个实践性很强的话题,涉及存储、计算、资源配置和查询编写等多个层面。以下是一些关键的优化技巧和最佳实践,适用于处理大规模数据集: ### 一、 数据存储与格式优化 1. **分区 (Partitioning):** * **技巧:** 将数据按逻辑列(如日期`dt`、地区`region`)划分存储。 * **优化:** 查询时只扫描相关分区数据,大幅减少I/O。 * **示例:** ```sql -- 创建分区表 CREATE TABLE logs ( user_id BIGINT, url STRING, ip STRING ) PARTITIONED BY (dt STRING); -- 查询特定日期的数据 SELECT * FROM logs WHERE dt = '2023-10-27'; ``` * **最佳实践:** 选择高频过滤的列作为分区键,避免创建过多小分区(小文件问题)。 2. **分桶 (Bucketing):** * **技巧:** 基于表某一列的哈希值,将数据均匀分布到固定数量的存储桶中。 * **优化:** 提高`JOIN`和`GROUP BY`效率,特别是等值连接/聚合。 * **示例:** ```sql -- 创建分桶表 CREATE TABLE users_bucketed ( user_id BIGINT, name STRING, ... ) CLUSTERED BY (user_id) INTO 32 BUCKETS; -- 分桶表Join更高效 SELECT a.*, b.* FROM orders a JOIN users_bucketed b ON a.user_id = b.user_id; ``` * **最佳实践:** 对于经常关联的大表定义相同数量和字段的分桶,启用`hive.optimize.bucketmapjoin`或`hive.optimize.bucketmapjoin.sortedmerge`选项效果更佳。 3. **选择合适的文件格式:** * **推荐:** **ORC** (Optimized Row Columnar) 或 **Parquet** (列式存储)。 * **优化:** * **列裁剪 (Column Pruning):** 只读取查询涉及的列。 * **高效压缩:** ORC和Parquet内置高压缩比算法。 * **谓词下推 (Predicate Pushdown):** 在读取文件时尽早应用过滤条件(`WHERE`中的部分过滤)。 * **索引:** ORC提供了轻量级内部索引(如布隆过滤器)。 * **最佳实践:** 优先选择ORC或Parquet作为生产环境数据存储格式。 4. **启用ORC布隆过滤器 (Bloom Filter):** * **技巧:** 在表/列级别为常用`WHERE`条件的列创建布隆过滤器。 * **优化:** 快速判断数据块中是否包含特定键值,显著减少不必要的IO读取。 * **示例:** ```sql CREATE TABLE my_orc_table ( id INT, name STRING, ... ) STORED AS ORC tblproperties ( "orc.bloom.filter.columns" = "id", "orc.bloom.filter.fpp" = "0.05" -- 期望假阳性概率 ); -- 针对id查询会利用布隆过滤器 SELECT * FROM my_orc_table WHERE id = 12345; ``` 5. **压缩数据:** * **技巧:** 配置表属性使用压缩算法(如Snappy, Zlib, LZO),特别是对于文本格式。 * **优化:** 减少磁盘I/O和网络传输开销。 * **最佳实践:** ORC/Parquet文件格式本身有压缩,通常无需额外配置。 ### 二、 查询编写优化 6. **`SELECT`语句只取所需列:** * **技巧:** **避免**使用`SELECT *`。 * **优化:** 启用列裁剪后,能显著减少读取和处理的数据量。对于列存格式效果极佳。 * **最佳实践:** 明确列出所有需要的列名。 7. **尽早过滤数据 (`WHERE` / `HAVING`):** * **技巧:** 在数据流向`JOIN`或`GROUP BY`之前,尽早应用过滤条件。 * **优化:** 减少后续处理阶段的数据量。谓词下推对此有很大帮助。 * **最佳实践:** ```sql -- 推荐:先过滤表A SELECT ... FROM ( SELECT * FROM big_table_a WHERE condition) a JOIN big_table_b b ON a.key = b.key; -- 避免:在JOIN后过滤表A SELECT ... FROM big_table_a a JOIN big_table_b b ON a.key = b.key WHERE a.condition; -- 此时a可能已通过JOIN膨胀了 ``` 8. **优化`JOIN`操作:** * **Map-side JOIN (`MapJoin`):** * **技巧:** 对小表(能完全加载进内存的表)使用MapJoin。将小表广播到所有Map任务。 * **优化:** 避免Reduce阶段开销和网络传输。 * **触发:** ```sql -- 方法1:设置自动转换小表 SET hive.auto.convert.join = true; SET hive.mapjoin.smalltable.filesize = 25000000; -- 设置小表阈值(默认约25MB) -- 方法2:显式提示 SELECT /*+ MAPJOIN(small_table) */ ... FROM big_table JOIN small_table ... ``` * **处理数据倾斜:** * **问题:** `JOIN`或`GROUP BY`时某些Key值特别多,导致一两个Reduce任务执行时间长。 * **解决方法:** * **设置并行任务数:** `SET mapreduce.job.reduces = N;` (N一般设为集群Reduce槽位总数的倍数)。 * **增加处理倾斜Key的Reducer数:** ```sql SET hive.groupby.skewindata=true; -- 针对GROUP BY倾斜 ``` * **手动分离倾斜Key:** ```sql -- 将倾斜Key单独处理 (假设id=0是倾斜Key) SELECT ... FROM ( SELECT ... FROM A JOIN B WHERE A.id != 0 UNION ALL SELECT ... FROM A JOIN B WHERE A.id = 0 AND B.id = 0) tmp; ``` * **使用`DISTRIBUTE BY` + `RAND()`打散:** ```sql -- 对Join键添加随机后缀 SELECT ... FROM ( SELECT id, ..., CAST(RAND() * 10 AS INT) AS salt FROM skewed_table ) a JOIN large_table b ON CONCAT(a.id, '_', a.salt) = b.id; -- 或者直接分布随机后缀 SELECT ... FROM skewed_table a JOIN large_table b ON a.id = b.id DISTRIBUTE BY RAND() SORT BY ...; -- 确保打散均匀 ``` 9. **优化聚合操作 (`GROUP BY`):** * **开启Map端部分聚合 (`HASHAGG`):** ```sql SET hive.map.aggr = true; -- 默认开启,在Map端做预聚合,减少Reduce端数据量 ``` * **使用`GROUPING SETS`, `ROLLUP`, `CUBE`代替多个查询:** 减少扫描次数。注意可能会增加Reducer负载。 * **处理`GROUP BY`倾斜:** 方法同`JOIN`倾斜处理(见第8)。 10. **使用`SORT BY`代替`ORDER BY`:** * `SORT BY`:在每个Reduce任务内部排序,输出局部有序结果(全局不一定有序)。 * `ORDER BY`:保证全局有序,但会将所有数据汇流到单个Reducer进行排序,极易成为瓶颈。 * **最佳实践:** 除非必须全局排序,否则优先使用`SORT BY`(通常与`DISTRIBUTE BY`配合)。如需全局有序且数据量极大,考虑分治或其他架构。 11. **使用`LIMIT`进行抽样:** * **技巧:** 在开发调试时,对表查询加`LIMIT`减少数据量加速响应。 * **注意:** `LIMIT`在子查询中的位置会影响优化器行为,有时需配合`ORDER BY NULL`避免排序。 12. **分析`EXPLAIN`输出:** * **技巧:** 在查询语句前添加`EXPLAIN`(或`EXPLAIN EXTENDED`, `EXPLAIN DEPENDENCY`, `EXPLAIN AUTHORIZATION`获取更详细信息)。 * **优化:** 理解Hive为查询生成的实际执行计划(MR或Tez),识别潜在的瓶颈或优化(如未使用分区、MapJoin未被触发等)。分析执行计划是性能调优的基础。 ### 三、 计算引擎与资源配置 (视引擎而定 - MR / Tez / Spark) 13. **选择合适的计算引擎:** * **Tez:** 推荐。基于DAG执行,避免重复读写HDFS,任务提交更快,资源申请更灵活(如动态容器复用)。通常性能优于MR。 * **Spark:** 另一种高性能DAG引擎,内存计算能力强。 * **MR:** 经典引擎,稳定性高,但调度开销相对大。 * **设置:** `SET hive.execution.engine=tez|mr|spark;` 14. **合理设置Mapper/Reducer数量:** * **目标:** 让每个Mapper处理合适的块大小(约等于HDFS块大小,如128MB),Reducer数量避免过大(导致小文件)或过小(导致倾斜或OOM)。 * **Mapper数:** 基本由输入文件数量和大小决定。调整`mapreduce.input.fileinputformat.split.minsize`/`maxsize`间接控制。 * **Reducer数:** * `mapreduce.job.reduces = N;` (直接指定)。 * Hive估算:`hive.exec.reducers.bytes.per.reducer` (默认约256MB)。 * 最大Reducers数:`hive.exec.reducers.max` (默认1009)。 * **最佳实践:** 优先让Hive自动估算,出现数据倾斜时再考虑手动干预。 15. **调整资源参数 (根据集群规模调整):** * Map/Reduce任务内存: ```sql SET mapreduce.map.memory.mb = 4096; -- 单个Map任务容器内存申请量 SET mapreduce.reduce.memory.mb = 8192; -- 单个Reduce任务容器内存申请量 SET mapreduce.map.java.opts = -Xmx3072m; -- Map任务JVM堆内存 (应小于总内存, 预留堆外开销) SET mapreduce.reduce.java.opts = -Xmx6144m; -- Reduce任务JVM堆内存 ``` * **最佳实践:** 监控任务是否因内存不足失败(查看GC日志或任务错误),逐步增加。避免设置过大导致资源碎片或OOM风险更高。充分利用YARN的调度能力。 ### 四、 元数据管理与统计分析 16. **收集表统计信息:** * **技巧:** 使用`ANALYZE TABLE`命令收集关键统计信息。 * **优化:** Hive CBO (Cost-Based Optimizer) 依赖这些信息生成更优的执行计划(如选择Join顺序,估算中间结果大小)。 * **示例:** ```sql ANALYZE TABLE my_table PARTITION(dt='2023-10-27') COMPUTE STATISTICS; -- 基础统计(行数、文件数) ANALYZE TABLE my_table PARTITION(dt='2023-10-27') COMPUTE STATISTICS FOR COLUMNS column1, column2; -- 列统计(NDV, min/max等) ``` * **最佳实践:** 定期或在数据发生重大变更后运行。对分区表收集分区级别的统计。 17. **使用`Vectorization` (向量化):** * **技巧:** 启用向量化查询执行。 * **优化:** 一次处理一批数据(向量),减少虚函数调用开销,利用现代CPU的SIMD指令。 * **设置:** ```sql SET hive.vectorized.execution.enabled = true; -- 启用向量化 SET hive.vectorized.execution.reduce.enabled = true; -- 启用Reduce端向量化(Hive较新版本才支持完善) ``` * **注意:** 主要对计算密集型操作(如过滤、聚合)效果好。ORC文件格式支持良好。需检查执行计划是否真正应用了向量化。 ### 五、 其他实践与小技巧 * **优化小文件:** * **问题:** HDFS上大量小文件增加NameNode压力,降低MR/Tez处理效率。 * **解决方法:** * 设置`hive.merge.mapfiles` / `hive.merge.mapredfiles` / `hive.merge.tezfiles` = true (在任务结束时合并小文件)。 * 设置`hive.merge.size.per.task` / `hive.merge.smallfiles.avgsize`定义合并大小阈值。 * 使用`INSERT OVERWRITE ...`语句重写表/分区时自动合并。 * 使用`hive --service concat`等工具手动合并。 * **使用中间表:** 将复杂查询拆分成多个步骤,使用中间表存储中间结果。方便调试、复用和优化各阶段。 * **避免多趟`COUNT(DISTINCT)`:** ```sql -- 低效:多次扫描数据 SELECT COUNT(DISTINCT col1), COUNT(DISTINCT col2) FROM table; -- 改进:使用单个查询计算多个distinct (Hive 2.0+支持一定程度优化,但复杂时仍需考虑改写为GROUP BY + COUNT或不同子查询) -- 或 -- 利用GROUP BY + CASE WHEN SELECT COUNT(DISTINCT CASE WHEN ... THEN col1 ELSE NULL END), COUNT(DISTINCT CASE WHEN ... THEN col2 ELSE NULL END) FROM table; ``` **总结:** Hive SQL性能优化是系统工程,需要: 1. **精心设计数据模型:** 利用分区、分桶、高效格式。 2. **编写高效查询:** 遵循列裁剪、谓词下推、优化JOIN/GROUP BY等原则。 3. **配置合适资源:** 匹配计算引擎与资源需求。 4. **启用统计与优化器:** 支持CBO做出最优决策。 5. **监控与分析:** 使用`EXPLAIN`理解计划,监控任务执行和资源使用情况。 持续应用这些技巧并结合具体场景实践,能显著提升Hive处理大数据的效率。 *** ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值