Hive 优化总结

Hive优化

本质:HDFS + MapReduce

问题原因:

  1. 倾斜
    • 分区:有的分区没有数据,有的分区数据堆积。(若按天分区,每一天数据差别大就叫倾斜。)
    • group by:有的分组键在表中数据很多,有的分组键数据很少。
    • 小表 join 大表:小表数据小,大表数据多,造成倾斜。

如何识别倾斜?

  • 若表为分区分桶表,以分区字段作为聚合条件聚合,并进行抽样。

  • 若有HDFS的权限,查看分区文件夹的大小是否存在明显差异。

  1. 过多

    • join过多导致job过多。
    • 小文件过多。
    • Mapper或Reducer过多。
  2. 使用不当

    • count(distinct) ❌ => select FIELD_NAME FROM ... GROUP BY FIELD_NAME;
    • join ... on ... where 谓词下推(见下文)。
    • select sum(field) from TABLE; 不支持全表聚合。
      • 可能的解决方法:

        sql 复制代码
        select sum(sub_total)
        from(
            select sum(order_amount) as sub_total
            from (
                select sum(order_amount),user_id%3 as id
                from TABLE_NAME
            )A group by id
        )A;

解决方案

数仓方案考虑整体性地解决问题

模型设计

  • 整体最优,考虑全局。

  • 合理减少表数量:

    • 数据建模:
      • 【星型】,雪花,星座。
      • 维度表(静态数据),事实表(动态数据:4W1H)。
      • 数仓需要将维度表"融进"事实表中,研究的是维度表中"变化"的数据。
      • 维度退化 => 星型。
    • sqoop|maxwell|cancal : query "select ... join ..."
    • ods -> dwd insert into ... select ... join ...
  • 充分了解业务,提前设计好预聚合。

    • 分层 => 轻量聚合(获取结果的时候可以不走MapReduce)。
    • 分区 => 避免交换。
      • 例如:如果表关联条件与分区依据一致,无需进行交换。
      • 如何选取分区字段?整体研究,选取最合适的共性字段。不一定要是商品ID,省市县三级分区等。分区尽量往业务上靠。
    • 分桶 => 拉链表(分桶表)、抽样。
      • "拉链表不一定是分区表,但一定要是分桶表":
        • 拉链表可以是非分区的,也就是说,不需要按照日期、地区等键值对历史数据进行分区。可以简单地存储历史数据。
        • 拉链表应该是分桶表,分桶有助于在执行数据合并、查找和分析操作时提高性能。
    • 压缩 => 减少体量(现在不太强调)。
      • 需考虑压缩和解压缩的成本是否大于时间消耗?
      • 压缩格式,表存储格式(是否支持压缩,是否支持切片)。

hadoop 内存管理

  • mapred
    • set mapreduce.map.memory.mb=256; Map任务内存。
    • set mapreduce.reduce.memory.mb=512; Reduce任务内存。
    • set mapreduce.map.java.opts=? Map的JVM。
    • set mapreduce.reduce.java.opts=? Reduce的JVM。
  • yarn
    • set yarn.nodemanager.resource.memory-mb=-1; NodeManager的内存。
    • set yarn.scheduler.minimum-allocation-mb=1024; YARN调度器的最小分配内存。
    • set yarn.scheduler.maximum-allocation-mb=8192; YARN调度器的最大分配内存。

倾斜:热点数据

  • join: 非大小表。
    • 原因:连接字段在连接表之间分布不均,或缺乏连接关系(两表的连接字段分配不均)。
    • 手动处理:连接键的选择(优先选择在数据的分布上相对均衡为连接键------通过抽样找到)。
    • 连接键拆分或随机映射(与其选取不均衡的列作为连接键,不如构建随即映射列------select user_id%3 as id group by id)。
    • 引入hash分区或分桶使得数据分布均衡。
    • 增加或减少任务的并行度。
  • map join: 大小表。
    • 默认true,即默认自动开启 mapjoin。
    • set hive.auto.convert.join=true;
    • 默认小表<=25M。
    • set hive.mapjoin.smalltable.filesize=25M;
    • 默认false,分桶表表mapjoin专用。
    • set hive.optimize.bucketmapjoin=true;
  • combiner: 默认true,即默认开启Mapper端聚合。
    • set hive.map.aggr=true;
  • groupby:HashPartitioner。
    • 默认-1,倾斜的倍数(倾斜度) n = 倾斜数据总均量/其他数据总均量 + (其他数据的差异数)。
    • 确定是否倾斜与倾斜程度:抽样(tablesample(bucket COUNT outof TOTAL))。
    • set mapreduce.job.reduces=n; (见下面 Reducer 数量控制)。
    • 默认false。
    • set hive.groupby.skewindata=true;

Mapper或Reduce输出过多小文件合并

若满足以下设置条件,任务结束后会单起MapReduce对输出文件进行合并。

  • 默认为true,map-only输出是否合并。
    • set hive.merge.mapfiles=true;
  • 默认为false,mapreduce输出是否合并。
    • set hive.merge.mapredfiles=true;
  • 默认256M,合并文件操作阈值,如果输入数据超过256M,则触发合并操作。
    • set hive.merge.size.per.task=256M;
  • 默认16M,合并文件平均大小小于该阈值则将他们合并为大文件。
    • set hive.merge.smallfiles.avgsize=16M;

控制Mapper和Reducer数量

Mapper

mapper的启动和初始化开销较大,数量过多导致开销大于逻辑处理,浪费资源。

  • 默认的Mapper数量:

    • int default_num = total_file_size/dfs.block.size;
  • 默认为2, 只有大于2时才会生效。

    • set mapreduce.job.maps=2;
  • Mapper数量有限值:

    • Math.max(min.split.size,Math.min(dfs.block.size,max.split.size))
    • 通过调整以下三项配置来调整Mapper数量。
      • 默认128M。
      • set dfs.block.size=128M;
      • 默认单个Mapper处理数据上限256M。
      • set mapred.max.split.size=256M;
      • 默认1字节。
      • set mapred.min.split.size=1;
  • 默认单个节点处理的数据下限1字节。

    • set mapred.min.split.size.per.node=1;
  • 默认单个机架处理的数据下限

    • set mapred.min.split.size.per.rack=1;
  • Mapper输入多个小文件合并后再切片

    • set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
  • Mapper切片的大小【越接近128M越好】?

    • set mapred.min.split.size = N;
    • 若文件过大,切片大小尽量调大。
    • 需要综合考虑Yarn的内存权限和分布式计算的均衡
  • 若表A单行内容量大,且处理逻辑复杂,需要将文件拆分(列裁剪,行筛选)

  • 将数据通过分区表拆分成更小粒度

  • 将数据随机且均匀地分散到不同的Reducer,这有助于均衡负载。

sql 复制代码
set mapreduce.job.reduces=3;
create table A_SPLITS as
select * from A distribute by rand(3);
Reducer
  • 默认-1,可以根据需要在客户端设置 :
    • int n = Math.min(SIZE/bytes.per.reducer, reducers.max) | num_partitions
    • set mapreduce.job.reduces=n;
  • 若存在数据倾斜,则 Hive 会单独分配Reducer处理倾斜数据
  • 若未设置 Reducer 数量,自动计算 Reducer 数量
  • 默认每个Reducer的数据为256M
    • set hive.exec.reducers.bytes.per.reducer=256M;
  • 默认单个任务最大Reducer数量(<1024台)
    • set hive.exec.reducers.max=1009;
  • Reducer只能为1的情况:
    • 没有使用 GROUP BY 子句来对数据进行分组,并且只是在原始数据上直接使用了聚合函数(如sum、count、max、min、avg、collect_list、concat_ws等)
      • 优化方案
      • select sum(sum_a) from (select sum(a) from A group by STH)T
    • 使用了order by
      • 优化方案
      • select * from (select * from A distribute by a sort by a) order by a;
  • 存在笛卡尔积,尽量不用
    • 若出现小表+大表的笛卡尔积:小表扩展join key,并根据需求复制 DN_COUNT 份,大表扩展join key,根据 DN_COUNT 随机生成
      • 关闭自动mapjoin : set hive.auto.convert.join=false
      • 设置reducer的数量为:set mapreduce.job.reduces=DN_COUNT

减少数据规模

调整存储格式
  • 设置建表格式
    • set hive.default.fileformat=orc|textfile|rcfile|sequencefile;
  • 压缩:为了减少Shuffle在局域网内数据交换产生的时间。
    • Mapper压缩
      • 开启Mapper输出压缩功能,默认false
        • set mapreduce.map.output.compress=true;
      • 设置Map输出数据的压缩方式:默认DefaultCodec
        • set mapreduce.map.output.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;
      • 设置任务过程输出是否压缩
        • set hive.exec.compress.intermediate=true;
    • Reducer压缩
      • 开启Reducer输出压缩功能;默认false
        • set hive.exec.compress.output=true;
      • reduce最终输出数据压缩;默认false
        • set mapreduce.output.fileoutputformat.compress=true;
      • reduce最终数据输出压缩为块压缩;默认RECORD
        • set mapreduce.output.fileoutputformat.compress.type=BLOCK;
      • reduce最终数据输出压缩方式;默认DefaultCodec
        • set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;
分区
  • 动态分区
    • 默认开启
      • set hive.exec.dynamic.partition=true;
    • 默认strict
      • set hive.exec.dynamic.partition.mode=nonstrict;
    • 默认最大动态分区数1000
      • set hive.exec.max.dynamic.partitions=1000;
    • 默认单节点最大动态分区数100
      • set hive.exec.max.dynamic.partitions.pernode=100;
    • 动态添加多分区数据(需要一张源数据表)
      • insert into table TABLE_PARTITION partition(partition_field) select *, partition_field from TABLE_SOURCE where ...;
  • 静态分区
    • 静态分区数据挂载
      • load data [local] inpath 'DATA_PATH' [overwrite|into] table TABLE_PARTITION partition(partition_field=VALUE);
    • 查看分区
      • show partitions TABLE_PARTITION;
    • 添加分区
      • alter table TABLE_PARTITION add partition(partition_field=VALUE);
    • 删除分区
      • alter table TABLE_PARTITION drop partition(partition_field=VALUE);

其他配置

  • count(distinct)

    • 不妥:select count(distict b) from TAB group by a
    • 稳妥:select count(b) from (select a,b from TAB group by a,b) group by a
  • CBO (COST BASED OPTIMIZER)

    • 默认为true
      • set hive.cbo.enable=true;
  • 分区裁剪

    • 以 on,where 多条件字段顺序,建【多重】分区表
  • 设置执行引擎,默认mr, tez|spark(优先)|DAG

    • set hive.execution.engine=tez;
  • 并行执行无依赖job

    • 默认false
      • set hive.exec.parallel=true;
    • 设置最大并行任务数,默认为8
      • set hive.exec.parallel.thread.number=8;
  • JVM重用(Hive3已取消,作了解即可)

    • 每个JVM运行的任务数
      • set mapreduce.job.jvm.numtasks = 8;
  • 本地化运算

    • 默认1,启动本地化模式reducer数量必须为0|1
      • set mapreduce.job.reduces=0/1;
    • 默认 yarn ,需设置为本地模式
      • set mapreduce.framework.name=local;
    • 开启自动本地化模式
      • set hive.exec.mode.local.auto=true;
    • 设置本地化文件数量上限,默认4
      • set hive.exec.mode.local.auto.input.files.max=4;
    • 默认128M,本地化文件大小上限
      • set hive.exec.mode.local.auto.inputbytes.max=128M;
    • 可能会导致内存溢出:java.lang.OutofMemoryError : java heap space
      • 修改 mv hive-env.sh.template hive-env.sh,去掉注释# export HADOOP_HEAPSIZE=1024
  • llap

    • 设置执行模式,默认container,2.0后扩展了llap
      • set hive.execution.mode=llap;
    • llap为DataNode常驻进程,混合模型,小型任务可以由llap解决,大任务由yarn容器执行
  • fetch

    • 默认mode,简单查询不走mr,直接提取
      • set hive.fetch.task.conversion=more;
  • 谓词下推(下推即优化的意思):确定主从表条件应该放在on后还是where后

    • 开启
      • set hive.optimize.ppd=true;
    • 规则
      • 左右外连接
        • 主表:on不可下推,where可下推
        • 从表:on可下推,where不可下推
      • 内连接
        • on和where都下推
      • 全外连接
        • on和where都不下推
相关推荐
酷爱码4 小时前
如何通过python连接hive,并对里面的表进行增删改查操作
开发语言·hive·python
Debug_TheWorld6 小时前
Hive学习
hive
weixin_307779138 小时前
Azure Data Factory ETL设计与调度最佳实践
数据仓库·性能优化·云计算·azure·etl
元63310 小时前
spark和hadoop之间的对比和联系
大数据·hadoop·spark
哥不是小萝莉12 小时前
Hadoop和Spark大数据挖掘与实战
hadoop·ai·spark
lix的小鱼14 小时前
spark和Hadoop之间的对比和联系
大数据·hadoop·spark
晴天彩虹雨16 小时前
Flink 数据清洗与字段标准化最佳实践
大数据·数据仓库·flink
TTBIGDATA16 小时前
如何将 Apache Hudi 接入 Ambari?完整部署与验证指南
大数据·hadoop·ambari·hudi·bigtop·湖仓·自定义组件集成
IT成长日记19 小时前
【Hive入门】Hive数据导出完全指南:从HDFS到本地文件系统的专业实践
hive·hadoop·hdfs·数据导出
向上的车轮20 小时前
数据湖DataLake和传统数据仓库Datawarehouse的主要区别是什么?优缺点是什么?
数据仓库