Hive基本调优

本文探讨Hive性能调优的关键策略,包括推测执行、并行执行、JVM重用等,深入分析Reduce阶段优化技巧,如调整reduce数量、处理数据倾斜及大表关联优化,旨在提升大数据处理效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

调优

推测执行

  1. 集群中NM/机器的负载是不一样
  2. 集群中机器的配置不同
  3. 数据倾斜
    一个job有100个reducer,其中99个很快运行完,只有最后一个花费很长的执行时间,那么这个job它的运行时长是取决于最慢的一个task,也就是长尾作业
    参数:hive.mapred.reduce.tasks.speculative.execution,默认是true

并行执行
并行的前提:多个task之间是没有依赖的
参数:hive.exec.parallel,默认是false
hive.exec.parallel.thread.number,打开并行度,一次执行的job个数,默认是8个

JVM重用
MapTask/ReduceTask其实都是以进程的方式运行的,那么有多少个task就会启动多少个JVM;当task运行完之后该JVM就会被销毁;JVM启动和销毁需要资源的开销;所有需要设置每个JVM可以执行多个task
参数:mapred.job.reuse.jvm.num.tasks,默认是1

Predicate Pushdown
打开谓词下推:hive.optimize.ppd=true

Reduce阶段优化

调整方式:
– set hive.exec.reducers.bytes.per.reducer
每个reduce能够处理的数据量大小,默认是256M
– set hive.exec.reducers.max
最大可以开启的reduce个数,默认是1099个
在只配了hive.exec.reducers.bytes.per.reducer以及hive.exec.reducers.max的情况下,实际的reduce个数 = InputFileSize / bytes per reducer来决定。
– set mapred.reduce.tasks=-1
实际运行的reduce个数,默认是-1,可以认为指定,但是如果认为在此指定了,那么就不会通过实际的总数据量/hive.exec.reducers.bytes.per.reducer来决定reduce的个数了

数据倾斜处理方案

1 设置参数
1)设置hive.map.aggr=true //开启map端部分聚合功能,就是将key相同的归到一起,减少数据量,这样就可以相对地减少进入reduce的数据量,在一定程度上可以提高性能,当然,如果数据的减少量微乎其微,那对性能的影响几乎没啥变化。
2)设置hive.groupby.skewindata=true //如果发生了数据倾斜就可以通过它来进行负载均衡。当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照Key 分布到 Reduce 中(这个过程是按照key的hash值进行分区的,不同于mr job1的随机分配,这次可以保证相同的Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。所以它主要就是先通过第一个mr job将key随机分配到reduce,使得会造成数据倾斜的key可能被分配到不同的reduce上,从而达到负载均衡的目的。到第二个mr job中,因为第一个mr job已经在reduce中对这些数据进行了部分聚合(就像单词统计的例子,a这个字母在不同的reduce中,已经算出它在每个reduce中的个数,但是最终的总的个数还没算出来,那么就将它传到第二个mr job,这样就可以得到总的单词个数),所以这里直接进行最后的聚合就可以了。
3)hive.exec.reducers.bytes.per.reducer= (单位是字节)
每个reduce能够处理的数据量大小,默认是256M
4)hive.exec.reducers.max=999
最大可以开启的reduce个数,默认是999个
在只配了hive.exec.reducers.bytes.per.reducer以及hive.exec.reducers.max的情况下,实际的reduce个数会根据实际的数据总量/每个reduce处理的数据量来决定。
5)mapred.reduce.tasks=-1
实际运行的reduce个数,默认是-1,可以认为指定,但是如果认为在此指定了,那么就不会通过实际的总数据量/hive.exec.reducers.bytes.per.reducer来决定reduce的个数了。

大表和大表的数据倾斜

HIVE Skewed Table

Skewed Table可以提高有一个或多个列有倾斜值的表的性能,通过指定经常出现的值(严重倾斜),hive将会在元数据中记录这些倾斜的列名和值,在join时能够进行优化。若是指定了STORED AS DIRECTORIES,也就是使用列表桶(ListBucketing),hive会对倾斜的值建立子目录,查询会更加得到优化。

可以再创建表是指定为 Skewed Table,如下例子,STORED AS DIRECTORIES是可选择的,它指定了使用列表桶(ListBucketing)

CREATE TABLE list_bucket_single (key STRING, value STRING)
  SKEWED BY (key) ON (1,5,6) [STORED AS DIRECTORIES];

你也可以使用多个列创建Skewed Table,如下使用两列

CREATE TABLE list_bucket_multiple (col1 STRING, col2 int, col3 STRING)
  SKEWED BY (col1, col2) ON (('s1',1), ('s3',3), ('s13',13), ('s78',78)) [STORED AS DIRECTORIES];

Hive Sort Merge Bucket Map Join(大表关联)

Hive 桶

对于每一个表(table)或者分区, Hive可以进一步组织成桶,
也就是说桶是更为细粒度的数据范围划分。Hive也是 针对某一列进行桶的组织。
Hive采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中。
把表(或者分区)组织成桶(Bucket)有两个理由:

(1)获得更高的查询处理效率。桶为表加上了额外的结构,Hive 在处理有些查询时能利用这个结构。
具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接 (Map-side join)高效的实现。
比如JOIN操作。对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作。
那么将保存相同列值的桶进行JOIN操作就可以,可以大大较少JOIN的数据量。

(2)使取样(sampling)更高效。在处理大规模数据集时,
在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,
会带来很多方便。

SKEW请参考:https://blog.youkuaiyun.com/mhtian2015/article/details/78931236
https://blog.youkuaiyun.com/yjgithub/article/details/66972966

<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处理大数据的效率。 *** ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值