重磅推荐
【产品特色】


【编辑推荐】

重点介绍了Hive性能调优所涉及的Hadoop组件和Hive工具

站在工程的角度介绍Hive性能调优,注重调优方法的可落地性

从语法、表模型设计、执行计划和计算引擎等多个角度进行讲解

注重实例演示和调优方法的总结,给出近百个实例带领读者实际操练

适用于Hive 1.x、Hive 2.x及Hive 3.x等多个版本

Hive性能调优实战》核心内容:

举例感受Hive性能调优的多样性

Hive问题排查与调优思路

环境搭建

Hive及其相关大数据组件

深入MapReduce计算引擎

Hive SQL执行计划

Hive数据处理模式

YARN日志

数据存储

发现并优化Hive中的性能问题

Hive知识体系总结

超值赠送:

《Hive性能调优实战》源代码文件(需要下载)


【内容简介】

《Hive性能调优实战》旨在介绍如何进行Hive性能调优,以及调优时所涉及的工具。书中重点介绍了Hive性能调优所涉及的Hadoop组件和Hive工具。考虑到很多调优方法的着眼点有一定的相似性,这些调优方法可以适用于多个Hive版本,所以《Hive性能调优实战》在介绍Hive的相关内容时会穿插Hive 1.x、Hive 2.x及Hive 3.x等多个版本的内容。

《Hive性能调优实战》共11章,涵盖的内容如下:

《Hive性能调优实战》内容实用,非常适合对大数据技术感兴趣的读者阅读,尤其是Hive进阶读者。另外,《Hive性能调优实战》还适合IT培训机构的大数据培训学员和高校相关专业的学生阅读。


【作者简介】

林志煌 曾在中国互联网头部公司长期从事大数据相关项目的研发。擅长并能够熟练使用Hive、MapReduce和Spark等大数据相关技术。经手过日数据流量TB级别和总量PB级别的Hadoop大数据平台建设。从事过数据采集、数据清洗、数据仓库模型构建及数据产品研发等工作,涵盖了数据生命周期的主要阶段。


【媒体评论】

市面上讲大数据技术的书很多,有些过于基础,有些又太理论化,实操价值不高。志煌的这本新书以Hive性能调优为主题,很实用,非常有价值,而且又能从Hadoop生态的整体视角来阐述和解决问题,填补了Hive性能调优类图书的空白。志煌长期在一线参与实际项目,其作品是他多年实战经验的总结,强烈推荐大数据行业的从业者阅读。

——进化星球CEO/百度无线搜索前负责人 胡嵩

志煌是一个对技术有着执着信念的人,很善于将一个问题“掰开揉碎”并由浅入深地讲述清楚。《Hive性能调优实战》就是这样一本可以让读者很容易理解的Hive性能调优图书,是一本不可多得的好书。《Hive性能调优实战》创造性地从Hadoop的全局告诉读者应该怎样做才能让Hive“更好玩”,特别适合正在学习Hive或正在用Hive做大数据分析的人员阅读。

——平安科技大数据平台数据集中组负责人 李钊

《Hive性能调优实战》系统地介绍了Hive性能调优的相关知识,书中在用实例讲解调优的同时,还特别注重方法论的总结和学习能力的培养,非常值得大数据技术爱好者阅读。

——字节跳动算法团队技术总监 丁锐

《Hive性能调优实战》结合作者多年的Hive调优经验,深入浅出地描述了Hive性能调优的理论基础和实践流程,相信能对你在Hive性能调优的思路上有所启发和帮助。

——聚信网络技术总监/百度91前高级技术总监 陈兴柏

这本书所介绍的Hive性能调优思路有点意思。作者首先带领读者梳理了Hive的技术生态和调优所需的基础理论,然后对Hive的技术栈进行了解剖,并对Hive性能调优做了介绍,有点有面,点面结合。赞!

——百度91无线数据组前负责人 林艳强

《Hive性能调优实战》由浅入深地介绍了Hive参与大数据系统的全过程。书中通过理论和实践相结合的方式,总结了Hive性能调优的方法和实用技巧,给使用Hive进行性能调优的人提供了一条学习的捷径,非常值得一读。

——无觅科技技术负责人 周宏斐


【目录】
第1章 举例感受Hive性能调优的多样性 1
1.1 感受改写SQL对性能的影响 1
1.1.1 数据准备 1
1.1.2 union案例 4
1.1.3 改写SQL实现union的优化 5
1.1.4 失败的union调优 8
1.2 感受调整数据块大小对性能的影响 10
1.2.1 数据准备 11
1.2.2 案例比较 11
1.3 感受不同数据格式对性能的提升 15
1.3.1 数据准备 15
1.3.2 案例比较 16
1.4 感受不同的表设计对性能的影响 18
1.4.1 数据准备 19
1.4.2 案例比较 21
1.5 调优其实不难 24
第2章 Hive问题排查与调优思路 25
2.1 小白推演Hive的优化方法 25
2.1.1 类比关系型数据库的调优 25
2.1.2 学习大数据分布式计算的基本原理 28
2.1.3 学习使用YARN提供的日志 31
2.1.4 干预SQL的运行方式 33
2.2 老工对Hive的调优理解 36
2.2.1 从一个过度优化案例说起 36
2.2.2 编码和调优的原则 43
2.2.3 Hive程序相关规范 49
2.3 总结调优的一般性过程 51
第3章 环境搭建 53
3.1 Docker基础 53
3.1.1 Docker介绍 54
3.1.2 安装Docker 56
3.1.3 常见的Docker使用与管理命令 58
3.1.4 使用Dockerfile构建服务镜像 60
3.1.5 Dockerfile语法 63
3.2 Cloudera Docker搭建伪分布式环境 66
3.3 Docker搭建分布式集群 68
3.3.1 构建JDK镜像 69
3.3.2 构建Hadoop镜像 70
3.3.3 构建Hive镜像 72
3.3.4 启动集群 73
3.4 CDM搭建分布式集群 75
3.4.1 Cloudera Manager组件 75
3.4.2 Docker构建软件安装内部源 76
3.4.3 CDM安装分布式集群 79
3.5 使用GitHub开源项目构建集群 87
第4章 Hive及其相关大数据组件 89
4.1 Hive架构 89
4.1.1 Hive 1.x版本基本结构 89
4.1.2 Hive元数据 91
4.2 YARN组件 97
4.2.1 YARN的优点 97
4.2.2 YARN基本组成 97
4.2.3 YARN工作流程 99
4.2.4 YARN资源调度器 100
4.3 HDFS架构 102
4.3.1 常见HDFS优化 102
4.3.2 HDFS基本架构和读写流程 103
4.3.3 HDFS高可用架构 105
4.3.4 NameNode联盟 107
4.4 计算引擎 109
4.4.1 MapReduce计算引擎 109
4.4.2 Tez计算引擎 111
4.4.3 LLAP长时在线与处理程序 113
4.4.4 Spark计算引擎 115
第5章 深入MapReduce计算引擎 117
5.1 MapReduce整体处理过程 117
5.2 MapReduce作业输入 118
5.2.1 输入格式类InputFormat 118
5.2.2 InputFormat在Hive中的使用 120
5.3 MapReduce的Mapper 121
5.3.1 Mapper类 121
5.3.2 Hive中与Mapper相关的配置 123
5.4 MapReduce的Reducer 125
5.4.1 Reducer类 126
5.4.2 Hive中与Reducer相关的配置 127
5.5 MapReduce的Shuffle 128
5.6 MapReduce的Map端聚合 129
5.6.1 Combiner类 129
5.6.2 Map端的聚合与Hive配置 130
5.7 MapReduce作业输出 131
5.7.1 OutputFormat作业输出 132
5.7.2 Hive配置与作业输出 133
5.8 MapReduce作业与Hive配置 134
5.9 MapReduce与Tez对比 135
5.9.1 通过案例代码对比MapReduce和Tez 135
5.9.2 Hive中Tez和LLAP相关的配置 141
第6章 HiveSQL执行计划 143
6.1 查看SQL的执行计划 143
6.1.1 查看执行计划的基本信息 144
6.1.2 查看执行计划的扩展信息 148
6.1.3 查看SQL数据输入依赖的信息 148
6.1.4 查看SQL操作涉及的相关权限信息 152
6.1.5 查看SQL的向量化描述信息 152
6.2 简单SQL的执行计划解读 158
6.3 带普通函数/操作符SQL的执行计划解读 161
6.3.1 执行计划解读 161
6.3.2 普通函数和操作符 162
6.4 带聚合函数的SQL执行计划解读 164
6.4.1 在Reduce阶段聚合的SQL 164
6.4.2 在Map和Reduce阶段聚合的SQL 167
6.4.3 高级分组聚合 169
6.5 带窗口/分析函数的SQL执行计划解读 172
6.6 表连接的SQL执行计划解读 175
6.6.1 Hive表连接的类型 175
6.6.2 内连接和外连接 176
6.6.3 左半连接 178
第7章 Hive数据处理模式 181
7.1 过滤模式 181
7.1.1 where子句过滤模式 182
7.1.2 having子句过滤 183
7.1.3 distinct子句过滤 184
7.1.4 表过滤 186
7.1.5 分区过滤 188
7.1.6 分桶过滤 189
7.1.7 索引过滤 191
7.1.8 列过滤 191
7.2 聚合模式 192
7.2.1 distinct模式 193
7.2.2 count(列)、count(*)、count(1)行计数聚合模式 194
7.2.3 可计算中间结果的聚合模式 197
7.2.4 不可计算中间结果的聚合模式 199
7.3 连接模式 200
7.3.1 普通Map连接 201
7.3.2 桶的Map连接和排序合并桶的Map连接 207
7.3.3 倾斜连接 209
7.3.4 表连接与基于成本的优化器 210
第8章 YARN日志 212
8.1 查看YARN日志的方式 212
8.1.1 ResourceManager Web UI界面 212
8.1.2 JobHistory Web UI界面 215
8.2 快速查看集群概况 216
8.2.1 Cluster Metrics集群度量指标 217
8.2.2 Cluster Node Metrics集群节点的度量信息 218
8.2.3 Cluster Overview集群概况 220
8.3 查看集群节点概况 221
8.3.1 节点列表概况 221
8.3.2 节点详细信息 223
8.3.3 节点作业信息 224
8.4 查看集群的队列调度情况 226
8.5 查看集群作业运行信息 230
8.5.1 集群作业运行状态 230
8.5.2 查看作业运行的基本信息 231
8.5.3 查看作业计数器 232
第9章 数据存储 236
9.1 文件存储格式之Apache ORC 236
9.1.1 ORC的结构 237
9.1.2 ORC的数据类型 238
9.1.3 ACID事务的支持 240
9.2 与ORC相关的Hive配置 241
9.2.1 表配置属性 241
9.2.2 Hive表的配置属性 241
9.3 文件存储格式之Apache Parquet 242
9.3.1 Parquet基本结构 243
9.3.2 Parquet的相关配置 245
9.4 数据归档 245
第10章 发现并优化Hive中的性能问题 247
10.1 监控Hive数据库的状态 247
10.2 监控当前集群状态 253
10.3 定位性能瓶颈 258
10.3.1 使用HS2 WebUI排除非大数据组件的问题 258
10.3.2 排查长时等待调度 260
10.3.3 Map任务读取小文件和大文件 261
10.3.4 Reduce的数据倾斜 262
10.3.5 缓慢的Shuffle 264
10.3.6 集群资源的限制 265
10.4 数据倾斜 266
10.4.1 不可拆分大文件引发的数据倾斜 266
10.4.2 业务无关的数据引发的数据倾斜 267
10.4.3 多维聚合计算数据膨胀引起的数据倾斜 268
10.4.4 无法削减中间结果的数据量引发的数据倾斜 268
10.4.5 两个Hive数据表连接时引发的数据倾斜 269
第11章 Hive知识体系总结 270
11.1 Hive知识体系 270
11.2 数据粒度 271
11.3 SQL相关 275
11.3.1 select查询语句 276
11.3.2 数据定义语言(DDL) 276
11.3.3 数据控制语言(DML) 279
11.3.4 用户自定义函数(UDF) 280
11.4 文件操作 281

【前言】

Hive作为Hadoop生态的重要组成部分,以其稳定和简单易用成为了当前企业在搭建大数据平台及构建企业级数据仓库时使用较为普遍的大数据组件之一。

目前,图书市场上关于Hive的书籍比较少,而专题介绍Hive性能调优的图书就更少了,几乎是个空白。有些书籍中涉及Hive性能调优,但也只是浅尝辄止。笔者认为,Hive是构建在Hadoop生态之上的,其性能调优其实与自身及其关联的大数据组件都有很密切的联系。鉴于市面上还没有从Hadoop的整体和全局介绍Hive性能调优的书籍,笔者编写了这《Hive性能调优实战》。这本书除了总结和完善自己的知识体系外,还希望能将自己多年的大数据开发经验系统地总结出来,供读者借鉴,从而让他们在学习和工作中少走弯路。

考虑到很多调优方法的着眼点有一定的相似性,这些方法一般可以适用于多个Hive版本,所以本书在讲解时穿插了Hive 1.x、Hive 2.x和Hive 3.x等多个版本的内容。

《Hive性能调优实战》特色

  1. 内容非常系统、实用

本书从语法、表模型设计、执行计划和计算引擎等多个角度系统地介绍了Hive性能调优的相关知识。为了避免纸上谈兵,书中在讲解知识点时列举了大量的实例帮助读者理解。

  1. 从原理谈优化

本书所介绍的实例都是从原理谈优化,让读者知其然也知其所以然。例如,在介绍HiveSQL调优时,我们会转换成计算引擎执行的等价代码,让读者知道HiveSQL的实际运行流程,从而直观地理解其可能引发的性能问题。

  1. 适用于多个Hive版本

本书总结了Hive性能调优的方法论,并总结了Hive性能调优需要关注的技术点。这些方法论和技术点无论是现在还是将来,只要是将Hive构建于Hadoop大数据平台之上,就都可以借鉴和使用。

《Hive性能调优实战》内容

第1章 举例感受Hive性能调优的多样性

本章用代码演示了各种优化技巧,从多个完全不同的角度介绍了Hive性能调优的多样性,例如改写SQL、调整数据存储的文件块、改变数据存储格式、设计Hive表等。

第2章 Hive问题排查与调优思路

本章介绍了Hive性能调优的整个过程,并给出了作者对于Hive调优过程中的一些思考,如编码和调优的原则、Hive SQL的相关开发规范等。通过阅读本章内容,读者可以对Hive性能调优的过程和工具有一个整体认识。

第3章 环境搭建

本章介绍了多种快速部署大数据开发环境的方式。考虑到不同读者手头的计算机资源有限,加之很多开发者并不喜欢“折腾”基础环境的搭建,书中介绍了一些比较快捷搭建环境的方式,涉及Docker和Cloudera Manager等技术。通过阅读本章内容,读者可以快速构建自己的大数据开发环境。

第4章 Hive及其相关大数据组件

本章比较系统地介绍了Hive及其相关大数据组件的基础知识。因为Hive构建于Hadoop大数据平台之上,其数据存储依赖HDFS,而HiveSQL的执行引擎依赖MapReduce、Spark和Tez等分布式计算引擎,其作业资源调度依赖YARN和Mesos等大数据资源调度管理组件,所以脱离Hadoop生态讲Hive性能调优无异于隔靴搔痒,解决不了根本问题。

第5章 深入MapReduce计算引擎

本章详细介绍了MapReduce计算引擎的相关内容。之所以选择MapReduce,首先是因为它足够简单,没有过多对高层接口做封装,而是将所有业务计算都拆分成Map和Reduce进行处理,易于读者理解;其次是因为大多数分布式计算框架处理数据的基本原理和MapReduce大同小异,学习MapReduce对于日后学习Spark和Tez有举一反三的效果。

第6章 Hive SQL执行计划

本章带领读者系统地学习了Hive SQL的相关知识。Hive SQL执行计划描绘了SQL实际执行的整体轮廓。通过执行计划,可以了解SQL程序在转换成相应的计算引擎时的执行逻辑。掌握了执行逻辑,就能更好地了解程序出现的瓶颈,从而便于用户更有针对性地进行优化。

第7章 Hive数据处理模式

本章介绍了Hive的数据处理模式。Hive SQL的语法多种多样,但是从数据处理的角度而言,这些语法本质上可以被分成三种模式,即过滤模式、聚合模式和连接模式。通过这些计算模式,读者可以了解它们的优缺点,从而提升SQL优化水平。

第8章 YARN日志

YARN日志是每个Hive调优人员必然会用到的工具。本章着重介绍了YARN日志,并对其进行解读。如果说执行计划提供了一个定性优化依据,那么YARN日志提供的就是一个定量优化依据。

第9章 数据存储

本章着重介绍了Hive数据存储的相关知识。数据存储是Hive操作数据的基础,选择一个合适的底层数据存储文件格式,即使在不改变当前Hive SQL的情况下,其性能也可以得到大幅提升。

第10章 发现并优化Hive中的性能问题

本章运用前面章节所介绍的性能问题定位工具,来定位Hive中常见的性能问题。对于Hive的使用者而言,借助Hadoop生态组件中所提供的工具就足以应对日常生产环境中所产生的问题。

第11章 Hive知识体系总结

本章简要梳理了Hive的整个知识体系,帮助读者比较全面地了解一项技术所涉及的方方面面,也有助于读者在学习该技术时形成自己的调优体系。

配书资料获取方式

《Hive性能调优实战》涉及的所有源代码需要读者自行下载。请在华章公司的网站www.hzbook.com上搜索到《Hive性能调优实战》,然后单击“资料下载”按钮,即可在《Hive性能调优实战》页面上找到下载链接。

《Hive性能调优实战》读者对象

? Hive初学者与进阶读者;

? 大数据开发工程师;

? 大数据开发项目经理;

? 专业培训机构的学员;

? 高校相关专业的学生。

……


【免费在线读】
第1章 举例感受Hive性能调优的多样性
谈及一项技术的优化,必然是一项综合性的工作,它是多门技术的结合。在这一章中将会用代码来演示各类优化技巧,目的在于演示Hive调优的多样性。
本章将从多个完全不同的角度来介绍Hive优化的多样性,如改写SQL、调整数据存储的文件块、改变数据的存储格式、Hive表的设计等方面。
如果对本章里面提到的技术和技巧不懂的话也没关系,先知道有这样一个技术即可。这些技术将会在本书后续章节陆续讲到,并会让读者知道为什么要用这些技巧,终能在知其然的情况下,知其所以然,不管在应对多大的数据量下都能够在开发、生产和运维等阶段感知和监控性能问题,并快速定位问题点将其快速解决。
1.1 感受改写SQL对性能的影响
通过改写SQL来优化程序性能是编程人员进行调优的常见手段。本节将围绕对union改写案例来让大家感受改写SQL对性能的提升。在1.1.2节中要思考一个问题:这样的写法性能瓶颈点在哪里?在1.1.3节中要思考两个问题:为什么选用这种改写方式?还有提升的空间吗?
1.1.1 数据准备
在做这个演示前需要准备一些数据,如果读者对准备数据代码不感兴趣,则可以略过本节的代码,直接学习后面章节的内容。案例1.1是准备学生信息表(student_tb_txt)数据的代码示例,案例1.2是准备学生选课信息表(student_sc_tb_txt)数据的代码示例。
【案例1.1】 生成Hive表student_tb_txt的数据。
(1)创建student数据的本地目录,并确保该目录有写权限,代码如下:

mkdir init_student
chmod -R 777 ./init_student

(2)在init_student目录下,创建生成student数据的python代码init_student.py,如下:

# coding: utf-8
import random
import datetime
import sys
reload(sys)
sys.setdefaultencoding('utf8')
# lastname和first都是为了来随机构造名称
lastname = u"赵李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏
水窦章云苏潘葛奚范彭郎鲁韦昌马苗"
firstname = u"红尘冷暖岁月清浅仓促间遗落一地如诗的句点不甘愿不决绝掬一份刻骨的思念
系一根心的挂牵在你回眸抹兰轩的底色悄然"
#创建一个函数,参数start表示循环的批次
def create_student_dict(start):
firstlen = len(firstname)
lastlen = len(lastname)
# 创建一个符合正太分布的分数队列
scoreList = [int(random.normalvariate(100, 50)) for _ in xrange(1, 5000)]
# 创建1万条记录,如果执行程序内存够大,这个可以适当调大
filename = str(start) '.txt'
print filename
#每次循环都创建一个文件,文件名为:循环次数 '.txt',例如 1.txt
with open('./' filename, mode='wr ') as fp:
for i in xrange(start * 40000, (start 1) * 40000):
firstind = random.randint(1, firstlen - 4)
model = {"s_no": u"xuehao_no_" str(i),
"s_name": u"{0}{1}".format(lastname[random.randint(1, lastlen - 1)],
firstname[firstind: firstind 1]),
"s_birth": u"{0}-{1}-{2}".format(random.randint(1991, 2000),
'0' str(random.randint(1, 9)),
random.randint(10, 28)),
"s_age": random.sample([20, 20, 20, 20, 21, 22, 23, 24, 25, 26], 1)[0],
"s_sex": str(random.sample(['男', '女'], 1)[0]),
"s_score": abs(scoreList[random.randint(1000, 4990)]),
's_desc': u"为程序猿攻城狮队伍补充新鲜血液,"
u"为祖国未来科技产业贡献一份自己的力量" * random.randint
(1, 20)}
#写入数据到本地文件
fp.write("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}".
format(model['s_no'], model['s_name'],
model['s_birth'], model['s_age'],
model['s_sex'], model['s_score'],
model['s_desc']))
# 循环创建记录,一共是40000*500=2千万的数据
for i in xrange(1, 501):
starttime = datetime.datetime.now()
create_student_dict(i)

(3)确保该文件有被执行的权限,代码如下:

chmod 777 init_student.py

(4)生成数据,执行下面的代码后将在init_student目录下生成500个txt文件。
python init_student.py

(5)创建hdfs目录:

hdfs dfs -mkdir /mnt/data/bigdata/hive/warehouse/student_tb_txt/

(6)在init_student目录下执行下面的命令,将所有的txt文件上传到步骤5创建的目录下。

hdfs dfs -put ./*.txt /mnt/data/bigdata/hive/warehouse/student_tb_txt/

(7)在Hive上创建student内部表,并将数据目录映射到步骤5的目录,代码如下:

create table if not exists default.student_tb_txt(
s_no string comment '学号',
s_name string comment '姓名',
s_birth string comment '生日',
s_age bigint comment '年龄',
s_sex string comment '性别',
s_score bigint comment '综合能力得分',
s_desc string comment '自我介绍'
)
row format delimited
fields terminated by '\t'
location '/mnt/data/bigdata/hive/warehouse/student_tb_txt/';

至此,student_tb_txt表的数据已经生成并加载完成。
【案例1.2】 生成Hive表student_sc_tb_txt的数据。
步骤与案例1.1类似,创建本地目录init_course,并编写student_sc_tb_txt表数据的生成程序init_course.py。代码如下:

# coding: utf-8
import random, datetime
import sys
reload(sys)
sys.setdefaultencoding('utf8')
#创建一个函数,参数start表示循环的批次
def create_student_sc_dict(start):
filename = str(start) '.txt'
print start
with open('./' filename , mode='wr ') as fp:
for i in xrange(start * 40000, (start 1) * 40000):
#课程出现越多表示喜欢的人越多
course = random.sample([u'数学', u'数学', u'数学', u'数学', u'数学',
u'语文', u'英语', u'化学', u'物理', u'生物'], 1)[0]
model = {"s_no": u"xuehao_no_" str(i),
"course": u"{0}".format(course),
"op_datetime": datetime.datetime.now().strftime("%Y-%m-%d"),
"reason": u"我非常非常非常非常非常非常非常"
u"非常非常非常非常非常非常非常喜爱{0}".format(course)}
line = "{0}\t{1}\t{2}\t{3}"\
.format(model['s_no'],
model['course'],
model['op_datetime'],
model['reason'])
fp.write(line)
# 循环创建记录,一共是40000*500=2千万记录
for i in xrange(1, 501):
starttime = datetime.datetime.now() # create_student_dict 转换成dataframe
格式,并注册临时表temp_student
create_student_sc_dict(i)

执行init_course.py代码并生成本地数据,之后创建hdfs目录,即/mnt/data/bigdata/hive/ warehouse/student_sc_tb_txt/,并将本地数据上传到对应的hdfs目录下,后创建对应的hive表,代码如下:

create table if not exists default.student_sc_tb_txt(
s_no string comment '学号',
course string comment '课程名',
op_datetime string comment '操作时间',
reason string comment '选课原因'
)
row format delimited
fields terminated by '\t'
location '/mnt/data/bigdata/hive/warehouse/student_sc_tb_txt';

至此已经完成了student_sc_tb_txt表数据的准备。准备好数据后,接着看下面的案例。
1.1.2 union案例
本节将编写一个带有union关键字的案例。在该案例中查询student_tb_txt表,每个年龄段晚出生和早出生的人的出生日期,并将其存入表student_stat中。
【案例1.3】 编写一个带有union关键字的案例。

--创建一张新的统计表
create table student_stat(a bigint, b bigint) partitioned by (tp string)
STORED AS TEXTFILE;
--开启动态分区
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
--找出各个年龄段早和晚出生的信息,并将这两部分信息使用union进行合并写入
student_stat中
insert into table student_stat partition(tp)
select s_age,max(s_birth) stat,'max' tp
from student_tb_txt
group by s_age
union all
select s_age,min(s_birth) stat, 'min' tp
from student_tb_txt
group by s_age;
--------------执行后计算结果如下-----------------------
Query ID = hdfs_20180928153333_6e96651d-e0d3-41a3-a3d9-121210094aca
Total jobs = 5
Launching Job 1 out of 5
…省略大部分的打印信息
MapReduce Jobs Launched:
Stage-Stage-1: Map: 84 Reduce: 328 Cumulative CPU: 1456.95 sec HDFS
Read: 21956521358 HDFS Write: 31719 SUCCESS
Stage-Stage-9: Map: 84 Reduce: 328 Cumulative CPU: 1444.21 sec HDFS
Read: 21956522014 HDFS Write: 31719 SUCCESS
Stage-Stage-2: Map: 6 Cumulative CPU: 34.47 sec HDFS Read: 258198 HDFS
Write: 582 SUCCESS
Stage-Stage-4: Map: 2 Cumulative CPU: 3.83 sec HDFS Read: 6098 HDFS
Write: 84 SUCCESS
Total MapReduce CPU Time Spent: 48 minutes 59 seconds 460 msec
OK
Time taken: 336.306 seconds

从上面打印的返回结果可以看到一个共有5个Job对应4个MapReduce(MR)的任务,即Stage-Stage-1、Stage-Stage-9、Stage-Stage-2和Stage-Stage-4对应的任务。
我们重点看黑体部分,从Total MapReduce CPU Time中可以看到所占用系统的实际耗时是48分59秒,用户等待的时间是336秒,记住这些数字,并对比接下来1.1.3节的结果。
扩展:Partition default.student_stat{tp=max} stats: […]这类信息只有在开启统计信息收集的时候才会打印出来,对应的配置是hive.stats.autogather,默认值是true。
?注意:Total MapReduce CPU Time Spent表示运行程序所占用服务器CPU资源的时间。而Time taken记录的是用户从提交作业到返回结果期间用户等待的所有时间。
接下来我们来看一个不使用union all的对比案例。
1.1.3 改写SQL实现union的优化
在上一节的案例中,我们只是完成了一个简单的求值/小值的统计,但却用了4个MR任务,如图1.1所示。

图1.1 union数据流(图中数字仅代表执行顺序)
(1)任务1,取student_tb_txt数据,计算birth的max值,并写入临时区。
(2)任务2,取student_tb_txt数据,计算birth的min值,并写入临时区。
(3)任务3,求任务1和任务2结果的并集。
(4)任务4,把任务3得到的结果写入student_stat中。
扩展:如何知道是上面4个任务,而不是其他任务呢?有两种方式可以判断:,通过查看执行计划,但是一定要记住一点,Hive的执行计划都是预测的,这点不像Oracle和SQL Server有真实的计划,在后面我们会详细谈谈执行计划;第二,按照SQL语法,结合MapReduce的实现机制去推测MR应该怎么实现,这种方式需要建立在有一定的MapReduce编写经验上,理解基本的分布式计算基本原理。
HiveSQL在执行时会转化为各种计算引擎能够运行的算子。作为HiveSQL的使用者,想要写出更加有效率的HiveSQL代码和MR代码,就需要去理解HiveSQL是如何转化为各种计算引擎所能运行的算子的。怎么做到?分为两步:步,理解基本的MR过程和原理,第二步,在日常编写SQL的过程中,尝试将SQL拆解成计算引擎对应的算子,拆解完和执行计划进行比对,还要和实际执行过程的算子比对,并思考自己拆解完后的算子与通过explain方式得到的算子的执行计划的异同。
在大数据领域,分布式计算和分布式存储会消耗大量的磁盘I/O和网络I/O资源,这部分资源往往成为了大数据作业的瓶颈。在运行案例1.3时观察集群资源的运行情况,将会发现CPU使用率很少,磁盘和网络读/写会变得很高,所以优化的焦点就集中在如何降低作业对I/O资源的消耗上。MR任务有一个缺点,即启动一次作业需要多次读/写磁盘,因为MR会将中间结果写入磁盘,而且为了保障磁盘的读写效率和网络传输效率,会进行多次排序。
如果一个SQL包含多个作业,作业和作业之间的中间结果也会先写到磁盘上。减少中间结果的产生,也就能够达到降低I/O资源消耗,提升程序效率。针对案例1.3,我们调优的关键点就是减少或者避免中间结果集的产生,基于这样的想法,改造后的数据流如图1.2所示。

图1.2 去掉union 数据流
观察图1.2,会发现有一处冗余的地方:step1和step2都是对student_tb_txt进行计算,但在计算时要查询两次表,这一步其实是冗余的。如果student_tb_txt是一个基数特别大的表,从表中取数(读取磁盘中的数据)的时间将变得很长,也浪费了集群宝贵的I/O资源。可以将其进行优化,变成只需读取一次表,如图1.3所示。

图1.3 优化从源表取数的操作
图1.3中优化了从源表student_tb_txt取数的次数,但是计算max值和min值却要分成两个MR作业,并将计算结果分别插入到student_stat中。如果能够在一个任务当中完成max和min值计算,那就可以减少启动一个作业的时间,以及MR任务对磁盘I/O和网络I/O的消耗。改造后的数据流如图1.4所示。

图1.4 简化max/min的计算过程
基于图1.4的方案,在Hive中采用了案例1.4的实现方式。
【案例1.4】 去掉union计算max和min。

DROP TABLE if EXISTS student_stat;
--创建student_stat
create table student_stat(a bigint, b bigint) partitioned by (tp string)
STORED AS TEXTFILE;
--开启动态分区
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
from student_tb_txt
INSERT into table student_stat partition(tp)
select s_age,min(s_birth) stat,'min' tp
group by s_age
insert into table student_stat partition(tp)
select s_age,max(s_birth) stat,'max’ tp
group by s_age;
-------------------执行结果-----------------------------------
Query ID = hdfs_20190225095959_25418167-9bbe-4c3d-aae0-0511f52ca683
Total jobs = 1
...//省略部分的打印信息
Hadoop job information for Stage-2: number of mappers: 84; number of
reducers: 328
2019-02-25 10:00:02,395 Stage-2 map = 0%, reduce = 0%
2019-02-25 10:00:19,973 Stage-2 map = 3%, reduce = 0%, Cumulative CPU 18.06 sec
...//省略部分的打印信息
2019-02-25 10:02:44,432 Stage-2 map = 100%, reduce = 100%, Cumulative CPU
2133.75 sec
MapReduce Total cumulative CPU time: 35 minutes 33 seconds 750 msec
Ended Job = job_1550390190029_0218
...
MapReduce Jobs Launched:
Stage-Stage-2: Map: 84 Reduce: 328 Cumulative CPU: 2133.75 sec HDFS
Read: 21957309270 HDFS Write: 2530 SUCCESS
Total MapReduce CPU Time Spent: 35 minutes 33 seconds 750 msec
OK
Time taken: 171.342 second

从以上结果中可以看到案例1.4的执行结果:
? Total MapReduce CPU Time:35分33秒;
? 用户等待时间:171秒。
相比于案例1.3,在案例1.4中通过改写SQL,即实现MULTI-TABLE-INSERT写法,Total MapReduce CPU Time Spent的总耗时从48分59秒减少到了35分33秒,减少了13分钟26秒,用户实际等待耗时从336秒减少到了171秒,节省了近1/2的时间。案例1.4中,整个优化的过程都集中在对磁盘I/O和网络I/O的优化上,在硬件资源保持不变的情况下,随着数据量的增加,整个集群的磁盘和网络压力会更大,相比于案例1.3的写法,其节省的时间会更加明显。
扩展:细心的读者会发现,案例1.4只启动了1个job,而案例1.3却启动了5个,每启动一个job,就说明集群多执行了一次MapReduce作业,MapReduce作业越多则代表数据就要经历更多次的磁盘读写和网络通信。随着数据量增多,磁盘和网络的负载会越来越大,耗在每个MapReduce过程的时间延迟也会越来越长。
1.1.4 失败的union调优
如果是从学PL-SQL或T-SQL刚转到学习Hive的读者,可能不知道MULTI-TABLE- INSERT的写法,在分析图1.2的调优方式时,将会采用如下案例来对其优化,即把一个union语句拆解成多个简单SQL,比如案例1.5的写法。
【案例1.5】 将一个含有union的SQL改写为两个简单的SQL。

drop table if exists student_stat;
create table student_stat(a bigint, b bigint) partitioned by (tp string)
STORED AS TEXTFILE;
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
--计算max值
insert into table student_stat partition(tp)
select s_age,max(s_birth) stat, 'max' tp
from student_tb_txt
group by s_age;
--计算min值
insert into table student_stat partition(tp)
select s_age,min(s_birth) stat,'min' tp
from student_tb_txt
group by s_age;

案例1.5将案例1.3的代码拆分成了两段代码,这样可以省略union的MR作业,计算max和min值的两个作业可直接将数据放到student_stat目录下,少了一次MR作业,并节省了一个MR所额外消耗的资源。看似很合理的方案,我们来看下两个程序的执行结果。
计算max值的执行结果:

Query ID = hdfs_20190219142020_2462a9ff-98d0-4fe0-afd8-24f78eebf46b
Total jobs = 1
Launching Job 1 out of 1
...//省略非必要信息
Hadoop job information for Stage-1: number of mappers: 84; number of
reducers: 328
2019-02-19 14:20:16,031 Stage-1 map = 0%, reduce = 0%
2019-02-19 14:20:26,350 Stage-1 map = 6%, reduce = 0%, Cumulative CPU 28.57 sec
...//省略部分信息
2019-02-19 14:22:35,773 Stage-1 map = 100%, reduce = 100%, Cumulative CPU
2028.24 sec
MapReduce Total cumulative CPU time: 33 minutes 48 seconds 240 msec
Ended Job = job_1550390190029_0057
Loading data to table default.student_stat partition (tp=null)
Time taken for load dynamic partitions : 153
Loading partition {tp=max}
Time taken for adding to write entity : 0
Partition default.student_stat{tp=max} stats: [numFiles=7, numRows=7,
totalSize=42, rawDataSize=35]
MapReduce Jobs Launched:
Stage-Stage-1: Map: 84 Reduce: 328 Cumulative CPU: 2028.24 sec HDFS
Read: 21956949222 HDFS Write: 1265 SUCCESS
Total MapReduce CPU Time Spent: 33 minutes 48 seconds 240 msec
OK
Time taken: 147.503 seconds

计算min值的执行结果:
Query ID = hdfs_20190219142222_7babcc53-6ceb-4ca6-a1c1-970d7caa43ab
Total jobs = 1
Launching Job 1 out of 1
...//省略部分信息
Hadoop job information for Stage-1: number of mappers: 84; number of
reducers: 328
2019-02-19 14:22:48,191 Stage-1 map = 0%, reduce = 0%
2019-02-19 14:22:56,453 Stage-1 map = 6%, reduce = 0%, Cumulative CPU 25.13 sec
...//省略部分信息
2019-02-19 14:25:11,062 Stage-1 map = 100%, reduce = 100%, Cumulative CPU
2014.8 sec
MapReduce Total cumulative CPU time: 33 minutes 34 seconds 800 msec
Ended Job = job_1550390190029_0058
Loading data to table default.student_stat partition (tp=null)
Time taken for load dynamic partitions : 104
Loading partition {tp=min}
Time taken for adding to write entity : 0
Partition default.student_stat{tp=min} stats: [numFiles=7, numRows=7,
totalSize=42, rawDataSize=35]
MapReduce Jobs Launched:
Stage-Stage-1: Map: 84 Reduce: 328 Cumulative CPU: 2014.8 sec HDFS
Read: 21956949222 HDFS Write: 1265 SUCCESS
Total MapReduce CPU Time Spent: 33 minutes 34 seconds 800 msec
OK
Time taken: 152.628 seconds

从案例1.5的执行结果中可以得到计算值和小值的Total MapReduce CPU Time总和为66分钟,用户等待时间为303秒。
案例1.5相比于union的写法,即案例1.3,系统CPU耗时多了近16分钟,这样的结果让人感到意外。难道分析过程有问题?其实对于Hive的早期版本,用案例1.5的代码确实是对案例1.3的代码进行了调优,但在Hive版本迭代中对union命令进行了优化,导致拆分后的代码令整个程序跑得慢了。从这里我们可以知道,某些调优方式随着版本的迭代,也逐渐变得不再适用。
扩展:Hive同其他数据库一样,提供了SQL并行执行的功能。但我们知道,SQL并行执行并不会节省作业耗用的CPU和磁盘资源,只是节省了用户等待的时间。另外,当作业足够大或者集群资源不够的情况下,SQL并不会并行运行。

返回顶部