• class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line">( timestamp date,
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">sessionID string,
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line">url string,
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line">source_ip string )
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line">STORED as ORC
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="7"> class="hljs-ln-code"> class="hljs-ln-line">tblproperties (“orc.compress" = "SNAPPY");
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="8"> class="hljs-ln-code"> class="hljs-ln-line">​
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="9"> class="hljs-ln-code"> class="hljs-ln-line">比如上边这个表,如果要获取每一个sessionID最新的访问记录,可以这样写:
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="10"> class="hljs-ln-code"> class="hljs-ln-line">​
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="11"> class="hljs-ln-code"> class="hljs-ln-line">SELECT clicks.*
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="12"> class="hljs-ln-code"> class="hljs-ln-line">FROM clicks inner join ( select sessionID, max(timestamp) as max_ts
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="13"> class="hljs-ln-code"> class="hljs-ln-line">from clicks
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="14"> class="hljs-ln-code"> class="hljs-ln-line">group by sessionID) latest
  • class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="15"> class="hljs-ln-code"> class="hljs-ln-line">ON clicks.sessionID = latest.sessionID and clicks.timestamp = latest.max_ts;
  • class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">

    语句解释:

    查看执行计划会发现该语句会产生两个MapReduce,第一个MapReduce生成最新的sessionID信息表,第二MapReduce做一个Join,前面输出的结果与原表进行Join得到最后的结果。

    再来看看下边的写法:

    1. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">SELECT *
    2. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line">FROM ( SELECT *,
    3. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">RANK() over (partition by sessionID, order by timestamp desc) as rank
    4. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line">FROM clicks
    5. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line">) ranked_clicks
    6. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line">WHERE ranked_clicks.rank=1;
    class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">

    语句解释:

    查看执行计划会发现只会有一个MapReduce,该语句将数据读出来对sesssionID进行分组并按时间进行倒序排序,最后过滤出排序后的第一条记录。

    比较第一个语句和第二个语句,发现第二个语句减少了一次MapReduce操作,性能自然大幅提升。很明显第一个语句多余执行了一次Join操作,第二语句以分组排序替代了Join,除去了不必要的Join操作,带来了性能的提升。

    5.2.2.3 Distinct聚合优化

    操作场景

    SELECT COUNT( DISTINCT id ) FROM TABLE_NAME ;

    优化前的问题:只有一个reduce处理全量数据,并发度不够,存在单点瓶颈。

    换种写法,reduce就会有多个,性能提升很多。

    SELECT COUNT(*) FROM (SELECT DISTINCT id FROM TABLE_NAME ) t;

    5.2.2.4 Order By

    操作场景

    默认情况下,Order By会生成一个Reduce进行全局排序,所以,一般要求Order By要加上Limit来使用,或者修改为Distribute By和Sort By 。

    对于必须进行全量,全局排序的,可以考虑对数据进行抽样后,通过Distribute By和Sort By进行并行排序。

    修改参数

    class="table-box">
    参数名描述
    hive.optimize.sampling.orderby默认是false
    hive.optimize.sampling.orderby.number默认1000,抽取多少个样本
    hive.optimize.sampling.orderby.percent默认0.1,每条记录生成一个随机数,当随机数小于这个值时,选中该记录

    5.2.2.5 Multi Insert

    对于从同一份源表往不同的表中插数据,可以采用multi insert语法,减少job个数

    1. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">FROM (SELECT a.status, b.school, b.gender
    2. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line"> FROM status_updates a JOIN profiles b
    3. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">
    4. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line">
    5. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line"> ON (a.userid = b.userid anda.ds='2009-03-20' )
    6. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line"> ) subq1
    7. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="7"> class="hljs-ln-code"> class="hljs-ln-line">
    8. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="8"> class="hljs-ln-code"> class="hljs-ln-line">
    9. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="9"> class="hljs-ln-code"> class="hljs-ln-line">INSERT OVERWRITE TABLE gender_summary PARTITION(ds='2009-03-20')
    10. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="10"> class="hljs-ln-code"> class="hljs-ln-line"> SELECT subq1.gender, COUNT(1) GROUP BY subq1.gender
    11. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="11"> class="hljs-ln-code"> class="hljs-ln-line">
    12. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="12"> class="hljs-ln-code"> class="hljs-ln-line">
    13. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="13"> class="hljs-ln-code"> class="hljs-ln-line">INSERT OVERWRITE TABLE school_summary PARTITION(ds='2009-03-20')
    14. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="14"> class="hljs-ln-code"> class="hljs-ln-line"> SELECT subq1.school, COUNT(1) GROUP BY subq1.school
    class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">

    上述查询语句使用了Multi-Insert特性连续insert了两张不同的表,减少了一轮MapReduce操作。

    5.2.2.6 动态分区+distribute by

    一般在从text转orc的时候,会用到动态分区,对于

    1. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">INSERT OVERWRITE TABLE T1 PARTITION(P1)
    2. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line">
    3. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">
    4. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line">
    5. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line">
    6. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line">
    7. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="7"> class="hljs-ln-code"> class="hljs-ln-line"> SELECT C1,C2,P1FROM T2
    class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">

    最好对分区字段加上distribute by,否则最坏的情况就是,一个Task包含每个分区的数据,往每个分区插入数据,导致Task的内存溢出。

    1. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">INSERT OVERWRITE TABLE T1 PARTITION(P1)
    2. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line">​
    3. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">​
    4. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line">​
    5. class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line">SELECT C1, C2, P1 FROM T2 DISTRIBUTE BY P1
    class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">

    6.1 性能调优常用方法

    6.1.1 表模型优化

    6.1.1.1 数据类型选择

    操作场景

    避免使用复杂的MAP,STRUCT等类型。

    字段类型要精确,例如是INTEGER型的,不要定义成STRING。精确的类型可以让计算机CPU更快的处理。

    6.1.1.2 表分区

    操作场景

    将数据分区,有助于减少查询时的数据量读取。例如按照天分区,那么按照时间跨度的查询,就可以通过排除掉一些分区,减少需要读取的文件数。

    分区支持多层,即对多个字段分区,支持动态分区,使用动态分区请参考7.4.2.5 Multi Insert章节的注意事项;

    单表的分区要尽量控制在1万以内,分区太多,会导致产生很多小文件,获取分区信息耗时等问题;

    修改参数

    class="table-box">
    参数名优化描述
    hive.exec.dynamic.partition.mode默认值strict,修改值nonstrict;
    hive.exec.max.dynamic.partitions默认值1000,最大动态partition的数量,可以根据分区的大小进行修改;
    hive.exec.max.created.files默认值100000,一个MR job中最多可以创建文件的数量;

    6.2.1 文件格式

    操作场景

    表存储的文件格式有文本格式,有列存储类型的格式,对于Hive,建议使用列存储类型ORC File,虽然转化成ORC文件类型,需要多一些时间,但是,会让后边查询操作节省大量的时间。

    修改参数

    class="table-box">
    参数名优化描述
    orc.compress默认值ZLIB

    6.3.1 小文件

    操作场景

    现在社区对于输入是小文件是没有自动合并功能,提供的合并命令,不够实用, 比如一个表有很多分区,需要对每个分区执行合并操作,我们暂时不建议用户用。

    现在如果输入有小文件要合并,我们是建议用户先原始数据加载到hive表中,再启动一个MR从小文件临时表插入最终表,这个过程即解决小文件也解决文件存储格式(ORC + Snappy)。

    对于输出,现在hive有参数控制多小的文件是小文件,对于输出的小文件是否要进行合并的参数,如下。

    对于顺序执行的作业链,只有最后一张表的数据需要持久化,中间临时结果用完就删除的情况,可以在最后生成结果表之前开启下面参数,防止之前的作业也会生成合并任务使作业变慢。

    修改参数

    class="table-box">
    参数名描述
    hive.merge.mapfiles默认为 True,是否合并 Map 输出文件
    hive.merge.mapredfiles默认为 False,是否合并 Reduce 输出文件
    hive.merge.size.per.task默认25610001000,合并后单个文件的大小
    hive.merge.smallfiles.avgsize默认16 * 1000 * 1000,文件平均大小小于该值时,认为需要合并

    6.4.1 压缩格式

    操作场景

    通常来说压缩会对性能有提升,虽然消耗了一点CPU,但是节省了磁盘IO,节省了网络带宽。

    对于ORC File文件,在定义表的时候,就指定了压缩类型。

    对于中间结果,一般是Sequence File类型,因此可以指定中间文件的压缩类型和压缩算法;

    修改参数

    class="table-box">
    参数名描述
    hive.exec.compress.output默认是false;对于文本文件格式,可以指定修改为true;orc文件类型不受此参数控制;
    mapred.output.compression.codec默认是org.apache.hadoop.io.compress.DeflateCodec,建议改成org.apache.hadoop.io.compress.SnappyCodec
    hive.exec.compress.intermediate中间结果是否进行压缩,默认是false
    hive.intermediate.compression.codec默认为空,建议改成org.apache.hadoop.io.compress.SnappyCodec

    6.5.1 并行度控制

    操作场景

    SQL会转换成Map和Reduce,Map的数量由总数据量除以Map处理的数据量来定,Reduce的数量是总数据量除以Reduce处理的数量,不超过最大值。

    修改参数

    class="table-box">
    参数名描述
    mapreduce.input.fileinputformat.split.maxsize默认256000000,map处理的最大数据量,一般不用改
    hive.exec.reducers.bytes.per.reducer默认256000000,reduce处理的最大数据量,一般不用改
    hive.exec.reducers.max默认999,对于集群比较大的情况,可以适当改大。

    6.6.1Task内存

    操作场景

    默认情况下,每个Map和Reduce使用的最大内存都是4GB,堆内存是3GB。

    原因如下: (说明格式:虽然分配了4G,但有时候任务实际可能只会到2G)

    1. hive当前每个map处理的数据量是这个参数mapreduce.input.fileinputformat.split.maxsize控制的, 默认是256MB, 通常情况下,这个256MB是压缩文件,由于压缩文件压缩率不定,解压后数据量不同,对内存就会有不同的需求。

    2. Hive有Mapjoin,Mapjoin是把小表加载到Map的内存中, 需要估计好加载后的内存,防止加载到内存过大导致内存溢出。

    3. 数据可能有些微的倾斜情况,导致Reduce等处理的数据量变大,如果内存小会导致失败。

    4. 对于大内存机器,可以尽量使用大的内存降低从内存刷磁盘的频率,这样可以减少IO。正确的做法应该是基于job的情况设置合适的内存,但是考虑到这样业务就需要花费更多的时间去调优, 因此配置成一个更大一点,更通用的值,保证job运行的稳定性。

    修改参数

    class="table-box">
    参数名描述
    mapreduce.map.memory.mb默认4096,Map的YARN内存申请数量,单位MB
    mapreduce.reduce.memory.mb默认4096,Reduce的YARN内存申请数量,单位mb
    mapreduce.map.java.opts默认-Xmx2048M -Djava.net.preferIPv4Stack=true,Map的java堆大小
    mapreduce.reduce.java.opts默认-Xmx3276M -Djava.net.preferIPv4Stack=true,Reduce的java堆大小
    yarn.app.mapreduce.am.resource.mb默认1536,MapReduce引擎下,AM占用的YARN内存资源总数,必须不小于堆大小设置
    yarn.app.mapreduce.am.command-opts默认值-Xmx1024m -XX:CMSFullGCsBeforeCompaction=1 -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -verbose:gc任务task数很多时,比如task超过3万时,需要考虑增加AM的内存,以免AM由于内存不足拖慢整个任务,可以把Xmx改成3072m;MapReduce引擎下,AM 堆大小设置

    7.1 其他性能调优常用方法

    7.1.1 启用CBO

    操作场景

    Hive默认是逻辑优化器,在多表join的情况下可以考虑启用CBO(cost based optimization),hive将会根据已经收集好的表统计信息,自动选择代价较小的join顺序来执行。

    CBO需要对表进行analysis,详细参考CPI文档。

    修改参数

    class="table-box">
    *参数名**描述*
    hive.cbo.enable默认是false,启用cbo需要修改成true;

    7.2.1 启用向量化

    操作场景

    现代CPU一般都支持SIMD(Single instruction, multiple data)等指令。通过向量化,可以使Hive利用现代CPU的这些高级特性,获得性能的提升

    修改参数

    class="table-box">
    *参数名**描述*
    hive.vectorized.execution.reduce.enabled默认是false,启用需要修改成true;

    最后

    谢谢大家 整理较为详细 推荐收藏哟

    @程序员500佰

    注:本文转载自blog.csdn.net的500佰的文章"https://blog.csdn.net/m0_57874805/article/details/145382064"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
    复制链接

    评论记录:

    未查询到任何数据!