目录
一、表设计层面优化
(一)合理设计表结构
包括选择合适的数据类型,避免使用过复杂的数据结构,这会增加序列化和反序列化的开销。
(二)使用分区表
解决 “数据按维度分组存储” 的问题,适合大范围数据过滤。
(三)使用分桶表
分桶数量=总的文件大小/(block size*2)
相关参数设置:
-- 启用自动分桶 join 优化 SET hive.optimize.bucketmapjoin=true; -- 启用自动排序桶表以进一步优化 SET hive.optimize.bucketmapjoin.sortedmerge=true;
适用条件:
- 连接键与分桶键一致:两表必须按相同的字段分桶,才能触发同桶连接优化。
- 大数据集场景:分桶对 GB 级以上数据集效果更显著,小数据集可能因分桶开销抵消优化收益。
限制:
- 分桶数需合理设置:桶数过少可能导致单桶数据量仍较大,桶数过多会增加文件管理开销。
- 需手动指定分桶键:建表时需明确分桶键,若业务场景变更(如连接键变化),可能需要重新分桶。
(四)分区 + 分桶——先分区再分桶(优化范围查询)
通过 “先分区过滤维度,再分桶定位分片”,将查询扫描范围从全表级缩小至分区内的桶级,大幅提升查询效率,尤其适用于多条件组合查询场景。
(五)存储格式
存储格式 | 文件结构 | 压缩支持 | 查询性能 | 适用场景 |
---|---|---|---|---|
TextFile (hive默认存储格式) |
纯文本,按行存储,格式简单,广泛兼容 | 支持(Gzip、BZip2 等) | 全表扫描慢,不支持列裁剪 | 数据导入临时表、小数据集 |
SequenceFile | 二进制键值对 | 支持(块压缩更佳) | 优于 TextFile,支持切片 | 中间数据存储、MR 作业输入输出 |
ORC | 列式存储 | 支持(Snappy、ZLIB) | 高性能,提供了高效数据压缩,减少存储空间 | 高频分析查询、大数据集 |
Parquet | 列式存储 | 支持(Snappy、Gzip) | 跨框架兼容,支持嵌套结构 | 大数据生态通用格式(Spark、Impala) |
Avro | 二进制格式 + Schema | 支持(Snappy、Deflate) | 支持 Schema 演进 | 数据交换、Schema 频繁变更场景 |
(六)压缩方式
二、数据倾斜层面优化
(一)什么是数据倾斜
在执行MapReduce作业时,部分reducer处理的数据量远大于其他reducer,这通常是由于某些键(如join操作中的join键或聚合操作中的分组键)的值特别多导致的。
数据倾斜可能导致查询性能下降,甚至作业失败。
(二)数据倾斜发生的原因
key值分布不均
(三)如何判断是否发生了数据倾斜
1、分析节点资源管理器,如果大部分节点已经执行完成,而个别节点长时间执行不完,很可能发生了数据倾斜。
2、分析执行日志,作业在reduce阶段停留在99%,很长时间完成不了,很可能发生了数据倾斜。
(四)数据倾斜优化办法
1.通常在数据处理的时候对null值进行赋默认值,当因为重复的空值导致表关联的数据倾斜,可以改写sql,将空值转换成随机值,方便在Map阶段均匀分配;
2.如果是因为数据类型不一致导致的数据倾斜,我们可以用CAST函数统一数据类型,来避免数据倾斜;
3.ORDER BY 造成的数据倾斜,可以用distribute BY 和 SORT BY 代替
4.GROUP BY 造成的数据倾斜,可以通过设置hive参数,设置负载均衡,也可以将倾斜的key单独筛选处理
5.关于负载均衡: 在Reduce阶段,可以通过设置`hive.exec.reducers.max`来限制最大的Reducer数量,避免某个点的数据过多造成性能瓶颈。
6.大表关联小表造成的,可以使用mapjoin将小表数据放在map端处理
7.大表关联大表终极解决方案是动态一分为二,即对倾斜的键值和不倾斜的键值分开处理,不倾斜的正常join即可,倾斜的把他们找出来做mapjoin,最后union all其结果即可。
但是此种解决方案比较麻烦,代码复杂而且需要一个临时表存放倾斜的键值。
8.小文件造成的数据倾斜,通过设置hive参数,在map输入端合并小文件,或者在map和reduce输出端合并
在map输入端合并小文件:(面试最常问)
使用`mapred.min.split.size`和`mapred.max.split.size`参数可以控制map输入分片的大小。增大这些分片的大小可以减少小文件的数量,从而减少map任务的数量。
例如:
set mapred.min.split.size=256000000;--(即256MB)
set mapred.max.split.size=256000000;--(即256MB)
在map和reduce输出端合并:
使用`mapred.merge.recordsBeforeMerge`参数可以设置在启动新的reduce任务之前,从map端发送的记录数。
增加这个值可以减少在reduce阶段之前需要合并的记录数。
例如:
set mapred.merge.recordsBeforeMerge=100000;在发送数据到reducer之前,map任务会累积100000条记录。
三、小文件问题
(一)小文件产生的原因
- 往动态分区表插入数据时,会插入大量小文件
- reduce的数量设置的较多,到reduce处理时,会分配到不同的reduce中,会产生大量的小文件
- 源数据文件就存在大量的小文件
- 分桶产生的小文件
(二)小文件问题解决方案
通过设置hive参数,在map输入端合并小文件,或者在map和reduce输出端合并
四、SQL语句层面
- 避免全表扫描: 尽量编写能够利用到分区和索引的查询语句,减少全表扫描的情况。
- 使用窗口(Analytic Functions): 在需要的时候使用窗口函数,比如`row_number()`, `rank()`等,可以有效减少JOIN操作。
- 优化JOIN操作: 尽量减少JOIN操作中的数据集大小,可以使用MAPJOIN或者STREAMING来减少数据的传输。
- 使用EXPLAIN计划: 通过EXPLAIN来查看查询计划,找出可能的性能瓶颈。
五、配置类型层面优化
- JVM重用: 通过设置参数如`hive.query.rewrite.mapjoins`, `hive.auto.convert.join`等,来控制Map-side join和自动转换普通JOIN到Map-side join。
- 内存调整: 调整Hive配置参数,如`hive.tez.container.size`(对于TEZ执行引擎),可以影响任务的并行度和资源使用。
- 执行器配置: 设置合理的`hive.exec.reducers.max`, `hive.exec.reducers.min`来控制减少阶段的数量。