大数据Hive学习之旅第六篇

562 阅读19分钟

「这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战」。

一、压缩和存储

1、Hadoop 压缩配置

1.1、MR 支持的压缩编码

image.png

为了支持多种压缩/解压缩算法,Hadoop 引入了编码/解码器,如下表所示:

image.png

压缩性能的比较:

image.png

1.2、压缩参数配置

要在 Hadoop 中启用压缩,可以配置如下参数(mapred-site.xml 文件中):

image.png

2、开启 Map 输出阶段压缩(MR 引擎)

开启 map 输出阶段压缩可以减少 job 中 map 和 Reduce task 间数据传输量。具体配置如下:

案例实操:

  1. 开启 hive 中间传输数据压缩功能

    hive (default)> set hive.exec.compress.intermediate=true;
    
  2. 开启 mapreduce 中 map 输出压缩功能

    hive (default)> set mapreduce.map.output.compress=true;
    
  3. 设置 mapreduce 中 map 输出数据的压缩方式

    hive (default)> set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
    
  4. 执行查询语句

    hive (default)> select count(ename) name from emp;
    

    查询Map任务日志中是否有snappy

    image.png

3、开启 Reduce 输出阶段压缩

当 Hive 将输出写入到表中时,输出内容同样可以进行压缩。属性hive.exec.compress.output控制着这个功能。用户可能需要保持默认设置文件中的默认值false,这样默认的输出就是非压缩的纯文本文件了。用户可以通过在查询语句或执行脚本中设置这个值为 true,来开启输出结果压缩功能。

案例实操:

  1. 开启 hive 最终输出数据压缩功能

    hive (default)> set hive.exec.compress.output=true;
    
  2. 开启 mapreduce 最终输出数据压缩

    hive (default)> set mapreduce.output.fileoutputformat.compress=true;
    
  3. 设置 mapreduce 最终数据输出压缩方式

    hive (default)> set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;
    
  4. 设置 mapreduce 最终数据输出压缩为块压缩

    hive (default)> set mapreduce.output.fileoutputformat.compress.type = BLOCK;
    
  5. 测试一下输出结果是否是压缩文件

    hive (default)> insert overwrite local directory '/opt/module/data/distribute-result' select * from emp distribute by deptno sort by empno desc;
    

    image.png

4、文件存储格式

Hive 支持的存储数据的格式主要有:TEXTFILE 、SEQUENCEFILE、ORC、PARQUET。

4.1、列式存储和行式存储

image.png

如图所示左边为逻辑表,右边第一个为行式存储,第二个为列式存储。

  1. 行存储的特点

    查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。

  2. 列存储的特点

    因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。

    TEXTFILE 和 SEQUENCEFILE 的存储格式都是基于行存储的;

    ORC 和 PARQUET 是基于列式存储的。

4.2、TextFile 格式

默认格式,数据不做压缩,磁盘开销大,数据解析开销大。可结合 Gzip、Bzip2 使用,但使用 Gzip 这种方式,hive 不会对数据进行切分,从而无法对数据进行并行操作。

4.3、Orc 格式

Orc (Optimized Row Columnar)是 Hive 0.11 版里引入的新的存储格式。

如下图所示可以看到每个 Orc 文件由 1 个或多个 stripe 组成,每个 stripe 一般为 HDFS的块大小,每一个 stripe 包含多条记录,这些记录按照列进行独立存储,对应到 Parquet中的 row group 的概念。每个 Stripe 里有三部分组成,分别是 Index Data,Row Data,Stripe Footer:

image.png

  1. Index Data:一个轻量级的 index,默认是每隔 1W 行做一个索引。这里做的索引应该只是记录某行的各字段在 Row Data 中的 offset。

  2. Row Data:存的是具体的数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个 Stream 来存储。

  3. Stripe Footer:存的是各个 Stream 的类型,长度等信息。

    每个文件有一个 File Footer,这里面存的是每个 Stripe 的行数,每个 Column 的数据类型信息等;每个文件的尾部是一个 PostScript,这里面记录了整个文件的压缩类型以及FileFooter 的长度信息等。在读取文件时,会 seek 到文件尾部读 PostScript,从里面解析到File Footer 长度,再读 FileFooter,从里面解析到各个 Stripe 信息,再读各个 Stripe,即从后往前读。

4.4、Parquet 格式

Parquet 文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此 Parquet 格式文件是自解析的。

  1. 行组(Row Group):每一个行组包含一定的行数,在一个 HDFS 文件中至少存储一个行组,类似于 orc 的 stripe 的概念。

  2. 列块(Column Chunk):在一个行组中每一列保存在一个列块中,行组中的所有列连续的存储在这个行组文件中。一个列块中的值都是相同类型的,不同的列块可能使用不同的算法进行压缩。

  3. 页(Page):每一个列块划分为多个页,一个页是最小的编码的单位,在同一个列块的不同页可能使用不同的编码方式。

    通常情况下,在存储 Parquet 数据的时候会按照 Block 大小设置行组的大小,由于一般情况下每一个 Mapper 任务处理数据的最小单位是一个 Block,这样可以把每一个行组由一个 Mapper 任务处理,增大任务执行并行度。Parquet 文件的格式。

    image.png

4.5、主流文件存储格式对比实验

从存储文件的压缩比和查询速度两个角度对比。

存储文件的压缩比测试:

测试数据

image.png

  1. TextFile

    • 创建表,存储数据格式为 TEXTFILE

      默认表存储数据格式为:TEXTFILE,默认可以不需要指定。

      create table log_text (
      track_time string,
      url string,
      session_id string,
      referer string,
      ip string,
      end_user_id string,
      city_id string
      )
      row format delimited fields terminated by '\t'
      stored as textfile;
      
    • 向表中加载数据

      hive (default)> load data local inpath '/opt/module/hive-3.1.2/datas/log.data' into table log_text;
      
    • 查看表中数据大小

      hive (default)> dfs -du -h /user/hive/warehouse/log_text;
      18.1 M  54.4 M  /user/hive/warehouse/log_text/log.data
      

      image.png

  2. ORC

    • 创建表,存储数据格式为 ORC

      create table log_orc(
      track_time string,
      url string,
      session_id string,
      referer string,
      ip string,
      end_user_id string,
      city_id string
      )
      row format delimited fields terminated by '\t'
      stored as orc
      tblproperties("orc.compress"="NONE"); -- 设置 orc 存储不使用压缩
      
    • 向表中加载数据

      hive (default)> load data local inpath '/opt/module/hive-3.1.2/datas/log.data' into table log_orc;
      

      orc格式存储的表数据不能通过load进行导入!!!

      image.png

      hive (default)> insert into table log_orc select * from log_text;
      
    • 查看表中数据大小

      hive (default)> dfs -du -h /user/hive/warehouse/log_orc/;
      7.7 M  23.1 M  /user/hive/warehouse/log_orc/000000_0
      

      image.png

  3. Parquet

    • 创建表,存储数据格式为 parquet

      create table log_parquet(
      track_time string,
      url string,
      session_id string,
      referer string,
      ip string,
      end_user_id string,
      city_id string
      )
      row format delimited fields terminated by '\t'
      stored as parquet;
      
    • 向表中加载数据

      hive (default)> insert into table log_parquet select * from log_text;
      
    • 查看表中数据大小

      hive (default)> dfs -du -h /user/hive/warehouse/log_parquet/;
      13.1 M  39.3 M  /user/hive/warehouse/log_parquet/000000_0
      

      image.png

5、存储和压缩结合

官网:cwiki.apache.org/confluence/…

ORC 存储方式的压缩:

image.png

注意:所有关于 ORCFile 的参数都是在 HQL 语句的 TBLPROPERTIES 字段里面出现

  1. 创建一个 ZLIB 压缩的 ORC 存储方式

    • 建表语句

      create table log_orc_zlib(
      track_time string,
      url string,
      session_id string,
      referer string,
      ip string,
      end_user_id string,
      city_id string
      )
      row format delimited fields terminated by '\t'
      stored as orc
      tblproperties("orc.compress"="ZLIB");
      
    • 插入数据

      insert into log_orc_zlib select * from log_text;
      
    • 查看插入后数据

      hive (default)> dfs -du -h /user/hive/warehouse/log_orc_zlib/ ;
      

      image.png

  2. 创建一个 SNAPPY 压缩的 ORC 存储方式

    • 建表语句

      create table log_orc_snappy(
      track_time string,
      url string,
      session_id string,
      referer string,
      ip string,
      end_user_id string,
      city_id string
      )
      row format delimited fields terminated by '\t'
      stored as orc
      tblproperties("orc.compress"="SNAPPY");
      
    • 插入数据

      insert into log_orc_snappy select * from log_text;
      
    • 查看插入后数据

      hive (default)> dfs -du -h /user/hive/warehouse/log_orc_snappy/;
      

      image.png

      ZLIB 比 Snappy 压缩的还小。原因是 ZLIB 采用的是 deflate 压缩算法。比 snappy 压缩的压缩率高。

  3. 创建一个 SNAPPY 压缩的 parquet 存储方式

    • 建表语句

      create table log_parquet_snappy(
      track_time string,
      url string,
      session_id string,
      referer string,
      ip string,
      end_user_id string,
      city_id string
      )
      row format delimited fields terminated by '\t'
      stored as parquet
      tblproperties("parquet.compression"="SNAPPY");
      
    • 插入数据

      hive (default)> insert into log_parquet_snappy select * from log_text;
      
    • 查看插入后数据

      hive (default)> dfs -du -h /user/hive/warehouse/log_parquet_snappy/;
      

      image.png

  4. 存储方式和压缩总结

    在实际的项目开发当中,hive 表的数据存储格式一般选择:orc 或 parquet。压缩方式一般选择 snappy,lzo。

二、企业级调优

1、执行计划(Explain)

  1. 基本语法

    EXPLAIN [EXTENDED | DEPENDENCY | AUTHORIZATION] query
    
  2. 案例实操

    • 查看下面这条语句的执行计划

    没有生成 MR 任务的

    hive (default)> explain select * from emp;
    OK
    Explain
    STAGE DEPENDENCIES:
      Stage-0 is a root stage
    
    STAGE PLANS:
      Stage: Stage-0
        Fetch Operator
          limit: -1
          Processor Tree:
            TableScan
              alias: emp
              Statistics: Num rows: 1 Data size: 6690 Basic stats: COMPLETE Column stats: NONE
              Select Operator
                expressions: empno (type: int), ename (type: string), job (type: string), mgr (type: int), hiredate (type: string), sal (type: double), comm (type: double), deptno (type: int)
                outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, _col6, _col7
                Statistics: Num rows: 1 Data size: 6690 Basic stats: COMPLETE Column stats: NONE
                ListSink
    
    

    有生成 MR 任务的

    hive (default)> explain select deptno, avg(sal) avg_sal from emp group by deptno;
    

    image.png

    • 查看详细执行计划

      hive (default)> explain extended select * from emp;
      hive (default)> explain extended select deptno, avg(sal) avg_sal from emp group by deptno;
      

2、Fetch 抓取

Fetch 抓取是指,Hive 中对某些情况的查询可以不必使用 MapReduce 计算。例如:SELECT * FROM employees;在这种情况下,Hive 可以简单地读取 employee 对应的存储目录下的文件,然后输出查询结果到控制台。

在 hive-default.xml.template 文件中 hive.fetch.task.conversion 默认是 more,老版本 hive 默认是 minimal,该属性修改为 more 以后,在全局查找、字段查找、limit 查找等都不走mapreduce。

image.png

案例实操:

  1. 把 hive.fetch.task.conversion 设置成 none,然后执行查询语句,都会执行 mapreduce程序。

    hive (default)> set hive.fetch.task.conversion=none;
    
    hive (default)> select * from emp;
    
    hive (default)> select ename from emp;
    
    hive (default)> select ename from emp limit 3;
    
  2. 把 hive.fetch.task.conversion 设置成 more,然后执行查询语句,如下查询方式都不会执行 mapreduce 程序。

    hive (default)> set hive.fetch.task.conversion=more;
    
    hive (default)> select * from emp;
    
    hive (default)> select ename from emp;
    
    hive (default)> select ename from emp limit 3;
    

3、本地模式

大多数的 Hadoop Job 是需要 Hadoop 提供的完整的可扩展性来处理大数据集的。不过,有时 Hive 的输入数据量是非常小的。在这种情况下,为查询触发执行任务消耗的时间可能会比实际 job 的执行时间要多的多。对于大多数这种情况,Hive 可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩短。

用户可以通过设置 hive.exec.mode.local.auto 的值为 true,来让 Hive 在适当的时候自动启动这个优化。

//开启本地 mr
set hive.exec.mode.local.auto=true; 

//设置 local mr 的最大输入数据量,当输入数据量小于这个值时采用 local mr 的方式,默认为 134217728,即 128M
set hive.exec.mode.local.auto.inputbytes.max=50000000;

//设置 local mr 的最大输入文件个数,当输入文件个数小于这个值时采用 local mr 的方式,默认为 4
set hive.exec.mode.local.auto.input.files.max=10;

案例实操:

  1. 关闭本地模式(默认是关闭的),并执行查询语句

    hive (default)> select count(*) from emp group by deptno;
    
  2. 开启本地模式,并执行查询语句

    hive (default)> set hive.exec.mode.local.auto;
    hive (default)> set hive.exec.mode.local.auto=true;
    hive (default)> select count(*) from emp group by deptno;
    

4、表的优化

4.1、小表大表 Join(MapJOIN)

将 key 相对分散,并且数据量小的表放在 join 的左边,可以使用 map join 让小的维度表先进内存。在 map 端完成 join。

实际测试发现:新版的 hive 已经对小表 JOIN 大表和大表 JOIN 小表进行了优化。小表放在左边和右边已经没有区别。

案例实操:

  1. 需求介绍

    测试大表 JOIN 小表和小表 JOIN 大表的效率

  2. 开启 MapJoin 参数设置

    • 设置自动选择 Mapjoin

      set hive.auto.convert.join = true; 默认为 true
      
    • 大表小表的阈值设置(默认 25M 以下认为是小表):

      set hive.mapjoin.smalltable.filesize = 25000000;
      
  3. MapJoin 工作机制

    image.png

  4. 建大表、小表和 JOIN 后表的语句

    // 创建大表
    create table bigtable(id bigint, t bigint, uid string, keyword string, 
    url_rank int, click_num int, click_url string) row format delimited 
    fields terminated by '\t';
    
    // 创建小表
    create table smalltable(id bigint, t bigint, uid string, keyword string, 
    url_rank int, click_num int, click_url string) row format delimited 
    fields terminated by '\t';
    
    // 创建 join 后表的语句
    create table jointable(id bigint, t bigint, uid string, keyword string, 
    url_rank int, click_num int, click_url string) row format delimited 
    fields terminated by '\t';
    
  5. 分别向大表和小表中导入数据

    hive (default)> load data local inpath '/opt/module/data/bigtable' into table bigtable;
    
    hive (default)>load data local inpath '/opt/module/data/smalltable' into table smalltable;
    
  6. 小表 JOIN 大表语句

    insert overwrite table jointable
    select b.id, b.t, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
    from smalltable s
    join bigtable b
    on b.id = s.id;
    
  7. 大表 JOIN 小表语句

    insert overwrite table jointable
    select b.id, b.t, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
    from bigtable b
    join smalltable s
    on s.id = b.id;
    

4.2、大表 Join 大表

注意:inner join 内连接在执行时,hive会自动对空 KEY 进行过滤,可以通过 explain 执行计划看出。所以空 KEY 过滤 针对的是非内连接!!!

  1. 空 KEY 过滤

    有时 join 超时是因为某些 key 对应的数据太多,而相同 key 对应的数据都会发送到相同的 reducer 上,从而导致内存不够。此时我们应该仔细分析这些异常的 key,很多情况下,这些 key 对应的数据是异常数据,我们需要在 SQL 语句中进行过滤。例如 key 对应的字段为空。

    案例实操:

    • 创建原始数据空 id 表

      // 创建空 id 表
      create table nullidtable(id bigint, t bigint, uid string, keyword string, 
      url_rank int, click_num int, click_url string) row format delimited 
      fields terminated by '\t';
      
    • 分别加载原始数据和空 id 数据到对应表中

      hive (default)> load data local inpath '/opt/module/data/nullid' into table nullidtable;
      
    • 测试不过滤空 id

      hive (default)> insert overwrite table jointable select n.* from nullidtable n left join bigtable o on n.id = o.id;
      
    • 测试过滤空 id

      hive (default)> insert overwrite table jointable select n.* from (select * from nullidtable where id is not null) n left join bigtable o on n.id = o.id;
      
  2. 空 key 转换

    有时虽然某个 key 为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在join 的结果中,此时我们可以表 a 中 key 为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的 reducer 上。

    案例实操:

    • 不随机分布空 null 值:

      • 设置 5 个 reduce 个数

        set mapreduce.job.reduces = 5;
        
      • JOIN 两张表

        insert overwrite table jointable select n.* from nullidtable n left join bigtable b on n.id = b.id;
        

        结果:如下图所示,可以看出来,出现了数据倾斜,某些 reducer 的资源消耗远大于其他 reducer。

        image.png

    • 随机分布空 null 值:

      • 设置 5 个 reduce 个数

        set mapreduce.job.reduces = 5;
        
      • JOIN 两张表

        insert overwrite table jointable select n.* from nullidtable n full join bigtable o on nvl(n.id,rand()) = o.id;
        

        结果:如下图所示,可以看出来,消除了数据倾斜,负载均衡 reducer 的资源消耗

        image.png

  3. SMB(Sort Merge Bucket join)

    • 创建第二张大表

      create table bigtable2(
       id bigint,
       t bigint,
       uid string,
       keyword string,
       url_rank int,
       click_num int,
       click_url string)
      row format delimited fields terminated by '\t';
      
      load data local inpath '/opt/module/data/bigtable' into table bigtable2;
      

      测试大表直接 JOIN

      insert overwrite table jointable
      select b.id, b.t, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
      from bigtable s
      join bigtable2 b
      on b.id = s.id;
      
    • 创建分通表 1,桶的个数不要超过可用 CPU 的核数

      create table bigtable_buck1(
       id bigint,
       t bigint,
       uid string,
       keyword string,
       url_rank int,
       click_num int,
       click_url string)
      clustered by(id) 
      sorted by(id)
      into 6 buckets
      row format delimited fields terminated by '\t';
      
      load data local inpath '/opt/module/data/bigtable' into table bigtable_buck1;
      
    • 创建分通表 2,桶的个数不要超过可用 CPU 的核数

      create table bigtable_buck2(
       id bigint,
       t bigint,
       uid string,
       keyword string,
       url_rank int,
       click_num int,
       click_url string)
      clustered by(id) 
      sorted by(id)
      into 6 buckets
      row format delimited fields terminated by '\t';
      
      load data local inpath '/opt/module/data/bigtable' into table bigtable_buck2;
      
    • 设置参数

      set hive.optimize.bucketmapjoin = true;
      set hive.optimize.bucketmapjoin.sortedmerge = true;
      set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
      
    • 测试

      insert overwrite table jointable
      select b.id, b.t, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
      from bigtable_buck1 s
      join bigtable_buck2 b
      on b.id = s.id;
      

4.3、Group By

默认情况下,Map 阶段同一 Key 数据分发给一个 reduce,当一个 key 数据过大时就倾斜了。

image.png

并不是所有的聚合操作都需要在 Reduce 端完成,很多聚合操作都可以先在 Map 端进行部分聚合,最后在 Reduce 端得出最终结果。

开启 Map 端聚合参数设置

  1. 是否在 Map 端进行聚合,默认为 True

    set hive.map.aggr = true;
    
  2. 在 Map 端进行聚合操作的条目数目

    set hive.groupby.mapaggr.checkinterval = 100000;
    
  3. 有数据倾斜的时候进行负载均衡(默认是 false)

    set hive.groupby.skewindata = true;
    

    当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。

    hive (default)> select deptno from emp group by deptno;
    

4.4、Count(Distinct) 去重统计

数据量小的时候无所谓,数据量大的情况下,由于 COUNT DISTINCT 操作需要用一个Reduce Task 来完成,这一个 Reduce 需要处理的数据量太大,就会导致整个 Job 很难完成,一般 COUNT DISTINCT 使用先 GROUP BY 再 COUNT 的方式替换,但是需要注意 group by 造成的数据倾斜问题.

案例实操

  1. 创建一张大表

    create table bigtable
    (id bigint,
    time bigint,
    uid string,
    keyword string,
    url_rank int,
    click_num int,
    click_url string) 
    row format delimited fields terminated by '\t';
    
  2. 加载数据

    hive (default)> load data local inpath '/opt/module/data/bigtable' into table bigtable;
    
  3. 设置 5 个 reduce 个数

    set mapreduce.job.reduces = 5;
    
  4. 执行去重 id 查询

    hive (default)> select count(distinct id) from bigtable;
    
  5. 采用 group by 去重 id

    select count(id) from (select id from bigtable group by id) a;
    

    虽然会多用一个 Job 来完成,但在数据量大的情况下,这个绝对是值得的。

4.5、笛卡尔积

尽量避免笛卡尔积,join 的时候不加 on 条件,或者无效的 on 条件,Hive 只能使用 1 个reducer 来完成笛卡尔积。

4.6、行列过滤

列处理:在 SELECT 中,只拿需要的列,如果有分区,尽量使用分区过滤,少用 SELECT *。

行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在 Where 后面,那么就会先全表关联,之后再过滤。

案例实操:

  1. 测试先关联两张表,再用 where 条件过滤

    hive (default)> select o.id from bigtable b
                  > join bigtable o on o.id = b.id
                  > where o.id <= 10;
    
  2. 通过子查询后,再关联表

    hive (default)> select b.id from bigtable b
                  > join (select id from bigtable where id <= 10) o on b.id = o.id;
    

4.7、分区

4.8、分桶

5、合理设置 Map 及 Reduce 数

  1. 通常情况下,作业会通过 input 的目录产生一个或者多个 map 任务。

    主要的决定因素有:input 的文件总个数,input 的文件大小,集群设置的文件块大小。

  2. 是不是 map 数越多越好?

    答案是否定的。如果一个任务有很多小文件(远远小于块大小 128m),则每个小文件也会被当做一个块,用一个 map 任务来完成,而一个 map 任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的 map 数是受限的。

  3. 是不是保证每个 map 处理接近 128m 的文件块,就高枕无忧了?

    答案也是不一定。比如有一个 127m 的文件,正常会用一个 map 去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果 map 处理的逻辑比较复杂,用一个 map任务去做,肯定也比较耗时。

    针对上面的问题 2 和 3,我们需要采取两种方式来解决:即减少 map 数和增加 map 数;

5.1、复杂文件增加 Map 数

当 input 的文件都很大,任务逻辑复杂,map 执行非常慢的时候,可以考虑增加 Map 数,来使得每个 map 处理的数据量减少,从而提高任务的执行效率。

增加 map 的方法为:根据 computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M 公式,调整 maxSize 最大值。让 maxSize 最大值低于 blocksize 就可以增加 map 的个数。

案例实操:

  1. 执行查询

    hive (default)> select count(*) from emp;
    Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 1
    
  2. 设置最大切片值为 100 个字节

    hive (default)> set mapreduce.input.fileinputformat.split.maxsize=100;
    Hadoop job information for Stage-1: number of mappers: 6; number of reducers: 1
    

5.2、小文件进行合并

  1. 在 map 执行前合并小文件,减少 map 数:CombineHiveInputFormat 具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat 没有对小文件合并功能。

    set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
    
  2. 在 Map-Reduce 的任务结束时合并小文件的设置:

    • 在 map-only 任务结束时合并小文件,默认 true

      set hive.merge.mapfiles = true;
      
    • 在 map-reduce 任务结束时合并小文件,默认 false

      set hive.merge.mapredfiles = true;
      
    • 合并文件的大小,默认 256M

      set hive.merge.size.per.task = 268435456;
      
    • 当输出文件的平均大小小于该值时,启动一个独立的 map-reduce 任务进行文件 merge

      set hive.merge.smallfiles.avgsize = 16777216;
      

5.3、合理设置 Reduce 数

  1. 调整 reduce 个数方法一

    • 每个 Reduce 处理的数据量默认是 256MB

      set hive.exec.reducers.bytes.per.reducer;
      
    • 每个任务最大的 reduce 数,默认为 1009

      set hive.exec.reducers.max;
      
    • 计算 reducer 数的公式

      N=min(参数 2,总输入数据量/参数 1)
      
  2. 调整 reduce 个数方法二

    在 hadoop 的 mapred-default.xml 文件中修改 设置每个 job 的 Reduce 个数

    set mapreduce.job.reduces = 15;
    
  3. reduce 个数并不是越多越好

    • 过多的启动和初始化 reduce 也会消耗时间和资源;

    • 另外,有多少个 reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

    • 在设置 reduce 个数的时候也需要考虑这两个原则:处理大数据量利用合适的 reduce 数;使单个 reduce 任务处理数据量大小要合适;

6、并行执行

Hive 会将一个查询转化成一个或者多个阶段。这样的阶段可以是 MapReduce 阶段、抽样阶段、合并阶段、limit 阶段。或者 Hive 执行过程中可能需要的其他阶段。默认情况下,Hive 一次只会执行一个阶段。不过,某个特定的 job 可能包含众多的阶段,而这些阶段可能并非完全互相依赖的,也就是说有些阶段是可以并行执行的,这样可能使得整个 job 的执行时间缩短。不过,如果有更多的阶段可以并行执行,那么 job 可能就越快完成。

通过设置参数 hive.exec.parallel 值为 true,就可以开启并发执行。不过,在共享集群中,需要注意下,如果 job 中并行阶段增多,那么集群利用率就会增加。

//打开任务并行执行
set hive.exec.parallel=true;
//同一个 sql 允许最大并行度,默认为8。
set hive.exec.parallel.thread.number=16;

当然,得是在系统资源比较空闲的时候才有优势,否则,没资源,并行也起不来。

7、严格模式

Hive 可以通过设置防止一些危险操作:

  1. 分区表不使用分区过滤

    将 hive.strict.checks.no.partition.filter 设置为 true 时,对于分区表,除非 where 语句中含有分区字段过滤条件来限制范围,否则不允许执行。换句话说,就是用户不允许扫描所有分区。进行这个限制的原因是,通常分区表都拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗令人不可接受的巨大资源来处理这个表。

  2. 使用 order by 没有 limit 过滤

    将 hive.strict.checks.orderby.no.limit 设置为 true 时,对于使用了 order by 语句的查询,要求必须使用 limit 语句。因为 order by 为了执行排序过程会将所有的结果数据分发到同一个Reducer 中进行处理,强制要求用户增加这个 LIMIT 语句可以防止 Reducer 额外执行很长一段时间。

  3. 笛卡尔积

    将 hive.strict.checks.cartesian.product 设置为 true 时,会限制笛卡尔积的查询。对关系型数据库非常了解的用户可能期望在 执行 JOIN 查询的时候不使用 ON 语句而是使用 where 语句,这样关系数据库的执行优化器就可以高效地将 WHERE 语句转化成那个 ON 语句。不幸的是,Hive 并不会执行这种优化,因此,如果表足够大,那么这个查询就会出现不可控的情况。

默认都是关闭的,如需要进行必要设置。

image.png

8、JVM 重用

9、压缩

三、友情链接

大数据Hive学习之旅第五篇

大数据Hive学习之旅第四篇

大数据Hive学习之旅第三篇

大数据Hive学习之旅第二篇

大数据Hive学习之旅第一篇