zoukankan      html  css  js  c++  java
  • Hive面试题(8)Hive调优全方位指南(三)

    来源:https://mp.weixin.qq.com/s/NdvHxOtVB7AS5P75QdVO7Q

    3、Hive架构层面

    3.1 启用本地抓取(默认开启)

    Hive 的某些 SQL 语句需要转换成 MapReduce 的操作,某些 SQL 语句就不需要转换成 MapReduce 操作,但是同学们需要注意,理论上来说,所有的 SQL 语句都需要转换成 MapReduce 操作,只不过Hive 在转换 SQL 语句的过程中会做部分优化,使某些简单的操作不再需要转换成 MapReduce,例如:

    1、只是 select * 的时候
    2、where 条件针对分区字段进行筛选过滤时
    3、带有 limit 分支语句时

    3.2 本地执行优化

    Hive 在集群上查询时,默认是在集群上多台机器上运行,需要多个机器进行协调运行,这种方式很好的解决了大数据量的查询问题。但是在 Hive 查询处理的数据量比较小的时候,其实没有必要启动分布式模式去执行,因为以分布式方式执行设计到跨网络传输、多节点协调等,并且消耗资源。对于小数据集,可以通过本地模式,在单台机器上处理所有任务,执行时间明显被缩短。

    三个参数:

    ## 打开hive自动判断是否启动本地模式的开关
    set hive.exec.mode.local.auto=true;
    ## map任务数最大值,不启用本地模式的task最大个数
    set hive.exec.mode.local.auto.input.files.max=4;
    ## map输入文件最大大小,不启动本地模式的最大输入文件大小
    set hive.exec.mode.local.auto.inputbytes.max=134217728;

    3.3 JVM重用

    Hive 语句最终会转换为一系列的 MapReduce 任务,每一个MapReduce 任务是由一系列的 MapTask和 ReduceTask 组成的,默认情况下,MapReduce 中一个 MapTask 或者 ReduceTask 就会启动一个JVM 进程,一个 Task 执行完毕后,JVM 进程就会退出。这样如果任务花费时间很短,又要多次启动JVM 的情况下,JVM 的启动时间会变成一个比较大的消耗,这时,可以通过重用 JVM 来解决

    JVM也是有缺点的,开启JVM重用会一直占用使用到的 task 的插槽,以便进行重用,直到任务完成后才会释放。如果某个 不平衡的job 中有几个 reduce task 执行的时间要比其他的 reduce task 消耗的时间要多得多的话,那么保留的插槽就会一直空闲却无法被其他的 job 使用,直到所有的 task 都结束了才会释放。

    根据经验,一般来说可以使用一个 cpu core 启动一个 JVM,假如服务器有 16 个 cpu core ,但是这个节点,可能会启动 32 个mapTask,完全可以考虑:启动一个JVM,执行两个Task

    3.4 并行执行

    有的查询语句,Hive 会将其转化为一个或多个阶段,包括:MapReduce 阶段、抽样阶段、合并阶段、limit 阶段等。默认情况下,一次只执行一个阶段。但是,如果某些阶段不是互相依赖,是可以并行执行的。多阶段并行是比较耗系统资源的。

    一个 Hive SQL 语句可能会转为多个 MapReduce Job,每一个 job 就是一个 stage,这些 Job 顺序执行,这个在 cli 的运行日志中也可以看到。但是有时候这些任务之间并不是是相互依赖的,如果集群资源允许的话,可以让多个并不相互依赖 stage 并发执行,这样就节约了时间,提高了执行速度,但是如果集群资源匮乏时,启用并行化反倒是会导致各个 Job 相互抢占资源而导致整体执行性能的下降。启用并行化

    ## 可以开启并行执行。
    set hive.exec.parallel=true;
    ## 同一个sql允许最大并行度,默认为8。
    set hive.exec.parallel.thread.number=16;

    3.5 推测执行

    在分布式集群环境下,因为程序Bug(包括Hadoop本身的bug),负载不均衡或者资源分布不均等原因,会造成同一个作业的多个任务之间运行速度不一致,有些任务的运行速度可能明显慢于其他任务(比如一个作业的某个任务进度只有50%,而其他所有任务已经运行完毕),则这些任务会拖慢作业的整体执行进度。为了避免这种情况发生,Hadoop采用了推测执行(Speculative Execution)机制,它根据一定的法则推测出“拖后腿”的任务,并为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果

    # 启动mapper阶段的推测执行机制
    set mapreduce.map.speculative=true;
    # 启动reducer阶段的推测执行机制
    set mapreduce.reduce.speculative=true;

    如果用户对于运行时的偏差非常敏感的话,那么可以将这些功能关闭掉。如果用户因为输入数据量很大而需要执行长时间的MapTask或者ReduceTask的话,那么启动推测执行造成的浪费是非常巨大大。其实我一般不使用

    3.6 Hive严格模式

    所谓严格模式,就是强制不允许用户执行有风险的 HiveQL 语句,一旦执行会直接失败。但是Hive中为了提高SQL语句的执行效率,可以设置严格模式,充分利用Hive的某些特点。

    ## 设置Hive的严格模式
    set hive.mapred.mode=strict;
    set hive.exec.dynamic.partition.mode=nostrict;

    注意:当设置严格模式之后,会有如下限制:

    1、对于分区表,必须添加where对于分区字段的条件过滤
    select * from student_ptn where age > 25
    2order by语句必须包含limit输出限制
    select * from student order by age limit 100;
    3、限制执行笛卡尔积的查询
    select a.*, b.* from a, b;
    4、在hive的动态分区模式下,如果为严格模式,则必须需要一个分区列式静态分区

    4、数据倾斜

    4.1 不同数据类型关联产生数据倾斜

    select * from users a
    left outer join logs b
    on a.usr_id = cast(b.user_id as string)

    4.2 空值过滤

    在生产环境经常会用大量空值数据进入到一个reduce中去,导致数据倾斜。

    解决办法:

    自定义分区,将为空的key转变为字符串加随机数或纯随机数,将因空值而造成倾斜的数据分不到多个Reducer。

    注意:对于异常值如果不需要的话,最好是提前在where条件里过滤掉,这样可以使计算量大大减少

    4.3 group by

    采用sum() group by的方式来替换count(distinct)完成计算。

    4.4 map join

    以上讲过了

    4.5 开启数据倾斜是负载均衡

    以上也讲过了

    5、调优方案

    5.1 日志表和用户表做链接

    select * from log a left outer join users b on a.user_id = b.user_id;

    users 表有 600w+ (假设有5G)的记录,把 users 分发到所有的 map 上也是个不小的开销,而且MapJoin 不支持这么大的小表。如果用普通的 join,又会碰到数据倾斜的问题。

    改进方案:

    select /*+mapjoin(x)*/ * from log a
    left outer join (
      select /*+mapjoin(c)*/ d.*
       from ( select distinct user_id from log ) c join users d on c.user_id =d.user_id
    ) x
    on a.user_id = x.user_id;

    假如,log 里 user_id 有上百万个,这就又回到原来 MapJoin 问题。所幸,每日的会员 uv 不会太多,有交易的会员不会太多,有点击的会员不会太多,有佣金的会员不会太多等等。所以这个方法能解决很多场景下的数据倾斜问题。

    5.2 位图法求连续七天发朋友圈的用户

    每天都要求 微信朋友圈 过去连续7天都发了朋友圈的小伙伴有哪些?假设每个用户每发一次朋友圈都记录了一条日志。每一条朋友圈包含的内容

    日期,用户ID,朋友圈内容.....
    dt, userid, content, .....

    如果 微信朋友圈的 日志数据,按照日期做了分区。

    2020-07-06 file1.log(可能会非常大)
    2020-07-05 file2.log

    解决方案:

    假设微信有10E用户,我们每天生成一个长度为10E的二进制数组,每个位置要么是0,要么是1,如果为1,代表该用户当天发了朋友圈。如果为0,代表没有发朋友圈。
    然后每天:10E / 8 / 1024 / 1024 = 119M左右
    求Join实现:两个数组做 求且、求或、异或、求反、求新增
  • 相关阅读:
    Linux 常用指令【持续更新】
    我的 MyBatis 实现的 Dao 层
    Mybatis 点点滴滴
    Redis 初识
    Mybatis 映射关系
    Mybatis 类属性和字段映射小小分析
    Mybatis 中 sql 语句的占位符 #{} 和 ${}
    使用 Maven 管理项目
    Maven 项目依赖 pom 文件模板
    Maven 私服安装和启动
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/14220834.html
Copyright © 2011-2022 走看看