Hive性能优化-原则与规范
Hive性能优化-原则与规范
从一个过度调优案例说起
下面有一个关于去重计数的代码,可以看看两种不同的去重方式
-- 第一种
select count(1) from (
select s_age
from student_tb_orc
group by a_age
) b
-- 第二种
select count(distinct s_age)
from student_tb_orc
可以发现第一种为了提前防止数据倾斜,做了map端的提前聚合,但是这样的子查询又会将原本一个job转化为两个job,这任务增加必会导致IO增加,其实也会造成运行时间变长。
会造成这样的情况,说明其有以下几点没有思考清楚:
- 进行驱虫的列是s_age列,它的业务含义表示年龄。其实他的枚举值是比较有限的,所以对于下游的reduce的个数也是比较有限,上游输入的数据也是有限,所以基本上很难出现数据倾斜。
- 第二种在使用distinct的时候,会在内存中构建一个hashtable,进行查找去重的时间复杂度是O(1),而group by在不同版本间变动比较的大,有的采取hashtable,有的采取排序,而排序最优时间复杂无法到O(1)
- 第一种方式会形成两个job,而第二种只会有一个job,而影响Hive性能最大的瓶颈往往就是I/O,所以多个job也就会产生多次的I/O。
- 还有在最新版本的Hive 3.0已经对count(distinct)优化了,其实就算是真的发生倾斜,也会自动将SQL逻辑进行改变。
- 最后就是第二段代码明显是比第一段代码要简洁,对于业务的解释更清晰,如果没有特殊的性能问题,代码简洁就是优。
编码和调优的原则
可能出现性能问题的几点
- 代码中看起来逻辑别扭,结构怪异的地方必然有其优化的空间。
- 需要经常进行特殊调整的需求,可能是对某方面没有理解到位的地方,可能是没有理解需求的意图,还有可能是需求有待商榷。
代码优化的原则
理透需求原则,这是优化的根本
作为需求开发方,我们需要对需求和业务有较深的理解,这才能做的对症下药和见病开方,这一个原则不只在进行代码优化,还在于整个大数据开发的每个环节上,一个成熟的大数据工程师就是50%的业务经理和20%的程序员,在进行开发的时候需要遵循不同实际业务需求场景选用不同的技术原则。
把握数据全链路原则,这是优化的脉络
业务人员提出一个需求时,我们需要知道相关的信息,大体可以分为以下3个方面:
业务数据准备
支持这个业务的基础数据有哪些?
输出的数据要求是什么?
这些业务的数据以什么样的方式存储?
其占用空间及元数据等信息如何?
快速了解Hive的元数据信息的方式有以下两种:
方式一:通过
desc formatted
来查看表的字段信息、表所属的数据信息、持有者、创建时间、最近修改时间、HDFS的路径信息;表的参数信息(Table Parameters),包括表的文件个数、数据量大小;表的存储信息(Storage Information),包括序列化/反序列化方式,输入/输出的文件格式,是否压缩、是否分桶等信息 方式二:查询Hive元数据
SELECT tbl_name, sum(case when param_key = 'numRows' then param_value else 0 end) '表的行数', sum(case when param_key = 'numRows' then 1 else 0 end) '表的分区数', sum(case when param_key = 'totalSize' then param_value else 0 end) / 1024 / 1024 / 1024 '数据量GB', sum(case when param_key = 'numFiles' then param_value else 0 end) '文件数' FROM hive_meta.PARTITIONS pt inner join PARTITION_PARAMS ptp on pt.PART_ID = ptp.PART_ID inner join hive_meta.TBLS tbl on pt.TBL_ID = tbl.TBL_ID - - -owner, 表的拥有者 where tbl_name in ('表名') and owner = '虚拟用户' group by tbl_name;
运行环境梳理
- 开发出一个业务程序后,需要清楚从提交到运行之前所需要的流程和环境,对需要的基础环境和资源要有一个简单的预估,确保程序能够正确且有足够的资源运行,其中关键是要了解大数据的资源管理和任务管理。
- 常见的资源管理组建有YARN和Mesos,常见的任务管理调度工具有Ozzie、Azkaban、airflow等
程序运行过程的数据流
- 需要知道Hive在运行HiveSQL时,数据在计算引擎各个阶段的变换形式和流转步骤,要理清这点,需要简单掌握以下几点:
- 理解HiveSQL的基本执行原理,通读它的执行计划,理解执行脉络,以及如何转化映射成MapReduce。
- 了解HiveSQL所用执行引擎的基本原理,以及在这个计算引擎内部各个节点的数据流,这需要全面读一读官网文档
- 了解Hive在运行时除了计算引擎之外所依赖的其他组件及其运行的基本原理,还需要了解Hive和这些组件之间的关联关系
- 需要知道Hive在运行HiveSQL时,数据在计算引擎各个阶段的变换形式和流转步骤,要理清这点,需要简单掌握以下几点:
坚持代码的简洁原则,这让优化更简单
简洁的代码是逻辑思维清晰的体现,也是对业务较好理解的一种体现,而读起来逻辑别扭、冗长复杂的代码,有更高的几率潜藏bug,潜藏性能不好的逻辑,甚至是对真实需求的一种扭曲实现。
没有瓶颈时谈论优化,是自寻烦恼
有优化意识是好,但优化不区分对象而谈优化,容易一叶障目。
可以将优化所要关注的问题分为两类:
- 影响项目整体落地的问题、重大性能问题。在实际工作,一般都是能够提前预知且提早介入,一般在项目设计阶段就已经规避
- 不影响项目整体落地,但是影响部分功能。在具体实现上,由于所处的环节较靠后,且和实际的业务有较强的关联,会根据实际情况而反复地调整。这种情况就需要在具体环境下,依据具体的业务要求进行调优,将优化放到有瓶颈点的地方去考虑和讨论,否则只是做更多的投入和产出不成正比的工作。
Hive程序相关规范
开发规范
- 单条SQL长度不宜超过一屏
- SQL子查询嵌套不宜超过3层
- 少用或者不用Hint
- 避免SQL代码的复制、粘贴。如果有多处逻辑一致的代码,可以将执行结果存储到临时表中。
- 尽可能使用SQL自带的高级命令做操作。例如,在多维统计分析中使用cube、grouping set和rollup等命令去替代多个SQL子句的union all
- 使用set命令,进行配置属性的更改,要有注释。
- 代码里面不允许包含对表/分区/列的DDL语句,除了新增和删除分区
- Hive SQL更加适合处理多条数据组合的数据集,不适合处理单条数据,且单条数据之间存在顺序依赖等逻辑关系。
- 保持一个查询语句所处理的表类型单一。例如,一个SQL语句中的表都是ORC类型的表,或者都是Parquet表。
- 关注NULL值的数据处理
- SQL表连接的条件列和查询的过滤列最好要有分区列和分桶列
- 存在多层嵌套,内层嵌套表的过滤条件不要写到外层
设计规范
- 表结构要有注释
- 列等属性字段需要有注释
- 尽量不要使用索引
- 创建内部表(托管表)不允许指定数据存储路径,一般由集群的管理人员统一规划一个目录并固化在配置中,使用人员只需要使用默认的路径即可
- 创建非接口表,只允许使用Orc或者Parquet,同一个库内只运行使用一种数据存储格式
- Hive适合处理宽边(列数多的表),适当的冗余有助于Hive的处理性能
- 表的文件块大小要与HDFS的数据块大小大致相
- 分区表分桶表的使用
命名规范
- 库/表/字段命名要自成一套体系
- 表以tb_开头
- 临时表以tmp_开头
- 视图以v_开头
- 自定义函数以udf_卡头
- 原始数据所在的库以db_org_开头,明细数据所在库以db_detail_开头, 数据仓库以db_dw_开头
总结调优的一般性过程
首先,要明白所有的优化是相对的,例如程序运行需要2个小时,看似很慢,但如果需求的目标是3个小时,即可正常作业,无特别情况,可以不进行优化,最后,从源头尽可能地掐断了因一个小需求引入众多操作所带来的性能问题
优化的基本流程如下:
- 第一,选择性能评估项及各自目标,常见的评估性能指标有程序的时延和吞吐量;
- 第二,如果系统是由多个组件和服务构成,需要分组件和服务定义性能目标;
- 第三,明确当前环境下各个组件的性能;
- 第四,分析定位性能瓶颈;
- 分析定位性能瓶颈:在Hive中最常见的是磁盘和网络I/O的瓶颈,其次是内存会成为一个性能瓶颈。
- 在Hive中,优化方式可以归结为3点,即优化存储、优化执行过程和优化作业的调度
- 第五,优化产生性能瓶颈的程序或者系统;
- 第六,性能监控和告警。
- 性能监控和告警:建立性能监控和告警,在操作系统和硬件层面可以借助Linux或UNIX系统提供的系统工具,也可以借助一些开源的工具,例如Zabbix和Ganglia