Hive性能调优实战(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-26 22:32:33

点击下载

作者:林志煌

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Hive性能调优实战

Hive性能调优实战试读:

前言

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

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

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

1.内容非常系统、实用

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

2.从原理谈优化

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

3.适用于多个Hive版本

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

第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的整个知识体系,帮助读者比较全面地了解一项技术所涉及的方方面面,也有助于读者在学习该技术时形成自己的调优体系。配书资料获取方式

本书涉及的所有源代码需要读者自行下载。请在华章公司的网站www.hzbook.com上搜索到本书,然后单击“资料下载”按钮,即可在本书页面上找到下载链接。本书读者对象

·Hive初学者与进阶读者;

·大数据开发工程师;

·大数据开发项目经理;

·专业培训机构的学员;

·高校相关专业的学生。本书作者

本书由林志煌编写。由于笔者的经验和能力所限,书中可能还有一些疏漏和不当之处,敬请读者指正,以便于及时改正。联系邮箱:hzbook2017@163.com。编著者第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_studentchmod -R 777 ./init_student(2)在init_student目录下,创建生成student数据的python代码init_student.py,如下:# coding: utf-8import randomimport datetimeimport sysreload(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 delimitedfields 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-8import random, datetimeimport sysreload(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 delimitedfields 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' tpfrom student_tb_txtgroup by s_ageunion allselect s_age,min(s_birth) stat, 'min' tpfrom student_tb_txtgroup by s_age;--------------执行后计算结果如下-----------------------Query ID = hdfs_20180928153333_6e96651d-e0d3-41a3-a3d9-121210094acaTotal jobs = 5Launching 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 SUCCESSStage-Stage-9: Map: 84 Reduce: 328 Cumulative CPU: 1444.21 sec HDFS Read: 21956522014 HDFS Write: 31719 SUCCESSStage-Stage-2: Map: 6 Cumulative CPU: 34.47 sec HDFS Read: 258198 HDFS Write: 582 SUCCESSStage-Stage-4: Map: 2 Cumulative CPU: 3.83 sec HDFS Read: 6098 HDFS Write: 84 SUCCESSTotal MapReduce CPU Time Spent: 48 minutes 59 seconds 460 msecOKTime 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_statcreate 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_txtINSERT into table student_stat partition(tp)select s_age,min(s_birth) stat,'min' tpgroup by s_ageinsert into table student_stat partition(tp)select s_age,max(s_birth) stat,'max’ tpgroup by s_age;-------------------执行结果-----------------------------------Query ID = hdfs_20190225095959_25418167-9bbe-4c3d-aae0-0511f52ca683Total jobs = 1...//省略部分的打印信息Hadoop job information for Stage-2: number of mappers: 84; number of reducers: 3282019-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 CPU2133.75 secMapReduce Total cumulative CPU time: 35 minutes 33 seconds 750 msecEnded Job = job_1550390190029_0218...MapReduce Jobs Launched:Stage-Stage-2: Map: 84 Reduce: 328 Cumulative CPU: 2133.75 sec HDFSRead: 21957309270 HDFS Write: 2530 SUCCESSTotal MapReduce CPU Time Spent: 35 minutes 33 seconds 750 msecOKTime 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' tpfrom student_tb_txtgroup by s_age;--计算min值insert into table student_stat partition(tp)select s_age,min(s_birth) stat,'min' tpfrom student_tb_txtgroup by s_age;

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

计算max值的执行结果:Query ID = hdfs_20190219142020_2462a9ff-98d0-4fe0-afd8-24f78eebf46bTotal jobs = 1Launching Job 1 out of 1...//省略非必要信息Hadoop job information for Stage-1: number of mappers: 84; number ofreducers: 3282019-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 CPU2028.24 secMapReduce Total cumulative CPU time: 33 minutes 48 seconds 240 msecEnded Job = job_1550390190029_0057Loading 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 : 0Partition 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 HDFSRead: 21956949222 HDFS Write: 1265 SUCCESSTotal MapReduce CPU Time Spent: 33 minutes 48 seconds 240 msecOKTime taken: 147.503 seconds

计算min值的执行结果:Query ID = hdfs_20190219142222_7babcc53-6ceb-4ca6-a1c1-970d7caa43abTotal jobs = 1Launching Job 1 out of 1...//省略部分信息Hadoop job information for Stage-1: number of mappers: 84; number ofreducers: 3282019-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 CPU2014.8 secMapReduce Total cumulative CPU time: 33 minutes 34 seconds 800 msecEnded Job = job_1550390190029_0058Loading 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 : 0Partition 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 HDFSRead: 21956949222 HDFS Write: 1265 SUCCESSTotal MapReduce CPU Time Spent: 33 minutes 34 seconds 800 msecOKTime 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并不会并行运行。1.2 感受调整数据块大小对性能的影响

改写SQL让程序性能得到了一定的优化,但不要把这个当成是Hive调优的全部。有时,即使应用程序不需要任何改变,也能让性能得到提高。本节我们就来看一下通过调整数据块大小对性能产生的影响。1.2.1 数据准备

我们先准备一个数据分布不均匀的表student_tb_txt_bigfile。数据准备的代码如下:#合并Map输出的小文件set hive.merge.mapfiles=true;set hive.merge.orcfile.stripe.level=true;set hive.merge.size.per.task=268435456;set hive.merge.smallfiles.avgsize=16777216;#先写入到student_tb_orc,由于orc格式会压缩数据因此最后生成的文件会很小#最后会被合并成3个文件create table student_tb_orc like student_tb_txt stored as orc;insert into student_tb_orcselect * from student_tb_txt;#再从student_tb_orc_写入student_tb_txt_bigfile#由于student_tb_orc的文件只有三个,写入到student_tb_txt_bigfile也只会有少数的几个文件#具体的文件数看启动的Map数(和集群配置有关),本例子最后生成了两个文件#即20Gtxt的表会被分到两个大文件中create table student_tb_txt_bigfile like student_tb_txt stored as textfile;insert into student_tb_txt_bigfileselect * from student_tb_orc;

student_tb_txt_bigfile表的数据准备好了,来看1.2.2节的案例。1.2.2 案例比较

参考案例1.4MULTI-INSERT的写法,我们编写了案例1.6,将SQL查询基表从student_tb_txt换成数据分布不均的表student_tb_txt_bigfile。【案例1.6】使用数据分布不均的表进行业务计算。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;--业务过程同案例1.4一样计算各个年龄段,最小出生日期和最大出生日期from student_tb_txt_bigfileINSERT into table student_stat partition(tp)select s_age,min(s_birth) stat,'min' statGROUP by s_ageinsert into table student_stat partition(tp)select s_age,max(s_birth) stat,'max' statGROUP by s_age;

下面是执行结果:Query ID = hdfs_20190225153030_259b6243-dac3-4f06-9325-e562843359d6Total jobs = 1Launching Job 1 out of 1...//省略非必要信息Hadoop job information for Stage-2: number of mappers: 82; number ofreducers: 3282019-02-25 15:31:09,605 Stage-2 map = 0%, reduce = 0%2019-02-25 15:31:27,114 Stage-2 map = 4%, reduce = 0%, Cumulative CPU 42.95 sec...//省略非必要信息2019-02-25 15:33:48,943 Stage-2 map = 100%, reduce = 100%, Cumulative CPU2158.91 secMapReduce Total cumulative CPU time: 37 minutes 58 seconds 910 msecEnded Job = job_1550390190029_0219...//省略非必要信息MapReduce Jobs Launched:Stage-Stage-2: Map: 82 Reduce: 328 Cumulative CPU: 2278.91 sec HDFSRead: 21967941496 HDFS Write: 2530 SUCCESSTotal MapReduce CPU Time Spent: 37 minutes 58 seconds 910 msecOKTime taken: 182.842 seconds

从上面的信息可以看到,案例1.6的执行结果为:

·Total MapReduce CPU Time:近38分;

·用户等待时间:182秒。

相比案例1.4,Total MapReduce CPU Time从35分钟增加到了38分钟。实际等待时间也从171秒增加到了182秒,性能有了些许下降。

为什么从student_tb_txt表执行的操作能够比student_tb_bigfile来得更快呢?我们来看两个表有什么不同。采用desc formmated table来查看student_tb_txt和student_tb_txt_bigfile两个表的基本信息,具体操作见案例1.7和案例1.8。【案例1.7】查看student_tb_txt表信息。>desc formatted student_tb_txt;…//省略非重要信息Location: hdfs://bigdata-03:8020/mnt/data/bigdata/warehouse/ student_tb_txtTable Type: MANAGED_TABLETable Parameters: COLUMN_STATS_ACCURATE false last_modified_by hdfs last_modified_time 1550503736 numFiles 500 numRows -1 rawDataSize -1 totalSize 21937135249 transient_lastDdlTime 1550503736…//省略非重要信息【案例1.8】查看student_tb_txt_bigfile。> desc formatted student_tb_txt_avg;…为节省篇幅,略去同上面相同的项目Location: hdfs://bigdata-03:8020/mnt/data/bigdata/warehouse/ student_tb_txt_bigfileTable Parameters: COLUMN_STATS_ACCURATE true numFiles 2 numRows 20000000 rawDataSize 21934950966 totalSize 21954950966 transient_lastDdlTime 1550542762…为节省篇幅,略去同上面相同的项目

比较案例1.7和案例1.8,在Table Parameters表中我们看到唯一的不同是numFiles,numFiles表示这个表存储数据的文件个数。

·student_tb_txt:存储约20GB的文件,用了500个文件,平均每个文件接近40MB。

·student_tb_txt_bifile:存储相同的数据,用了2个文件,平均每个文件10GB。

我们用hdfs dfs命令来查看具体的文件信息,并验证上面的信息,见案例1.9。【案例1.9】查看HDFS中的数据文件。#下面的…表示省略相同的路径前缀#查看表student_tb_txt的文件信息$ hdfs dfs -ls /mnt/data/bigdata/warehouse/student_tb_txtFound 500 items-rwxrwxrwt 3 hue hive 43840864 2018-09-27 17:53 1.txt-rwxrwxrwt 3 hue hive 43905110 2018-09-27 17:49 2.txt…-rwxrwxrwt 3 hue hive 43755002 2018-09-27 17:46 500.txt#查看表student_tb_txt_bigfile的文件信息$ hdfs dfs -ls /mnt/data/bigdata/warehouse/student_tb_txt_bigfileFound 2 items-rwxrwxrwt 3 hdfs hive 16158758810 2018-11-09 10:33 000000_0-rwxrwxrwt 3 hdfs hive 5796192156 2018-11-09 10:33 000001_0

从hdfs的命令结果中我们确实可以看到student_tb_txt_bigfile有两个文件,最大约16GB,最小约5GB,student_tb_txt共有500个文件,每个文件大约40MB。

接着再来看看运行案例1.4和1.6启用的Map数,在案例1.4的打印输出中会看到这样的一段结果:Stage-Stage-2: Map: 84 Reduce: 328 Cumulative CPU: 2133.75 sec HDFSRead: 21957309270 HDFS Write: 2530 SUCCESS

上面的信息表示案例1.4读500个文件用了84个Map任务。在案例1.6的打印输出中还会看到相同的一段结果:Stage-Stage-2: Map: 82 Reduce: 328 Cumulative CPU: 2278.91 sec HDFSRead: 21967941496 HDFS Write: 2530 SUCCESS

上面的信息表示案例1.6读取2个文件用了82个Map任务。仅仅2个文件,却启动了82个任务,这在读取文件时会存在如图1.5所表现的问题,一个文件要被分布在不同服务器的Map任务中读取。图1.5 Map读取大文件

而案例1.6的Map实际工作情况则如图1.6所示,单个文件只被本地的Map所读取。图1.6 Map读取普通文件

两者最大的区别就在于,案例1.4在读文件时需要跨网络传输,而案例1.6只有本地读写,这是两者差异的最直接原因。在后面的章节中,我们将会通过一些量化的数据再来剖析这个案例。扩展:案例1.4相比案例1.6其提升的效率不是很明显,在不同的环境下,执行结果可能会有点差异,比如当集群的规模较小,整个集群的内网带宽资源充裕的情况下,因网络传输带来的时间损耗和其他原因(例如,hdfs的namenode节点资源紧张,定位查找更多的文件需要耗费更多时间等)相比所造成的时间延迟可能还少的时候,案例1.6代码的执行速度可能会比案例1.4代码稍快。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载