代码改变世界

数据倾斜及小文件合并

2020-04-23 10:55  DataBases  阅读(628)  评论(0编辑  收藏  举报

数据倾斜
常见表现:
在 hive 中 map 阶段早就跑完了, reduce 阶段一直卡在 99% 。很大情况是发生了数据倾斜,整个任务在等某个节点跑完。
在spark 中大部分的 task 执行的特别快,剩下的一些 task 执行的特别慢,要几分钟或几十分钟才执行完一个 task
Hive中大表 join 的时候,容易产生数据倾斜问题, spark 中产生 shuffle 类算子的操作, groupbykey 、 reducebykey 、 join 等操作会引起数据倾斜。
通过 stage 去定位
数据倾斜原因:
在进行shuffle 的时候,必须 将各个节点上相同的 key 拉取到某个节点上的一个 task 来进行处理 ,比如按照 key 进行聚合或 join 等操作。此时如果某个key 对应的数据量特别大的话,就会发生数据倾斜。比如大部分 key 对应 10 条数据,但是个别 key 却对应了 100 万条数据,那么大部分 task 可能就只会分配到 10 条数据,然后 1 秒钟就运行完了;但是个别 task 可能分配到了 100 万数据,要运行一两个小时
数据倾斜解决方案
解决方法
1 :直接过滤掉那些引起倾斜的 key
例如
select key1,count(*) as num_1
from
dw.table_a
group by key1
order by num_1 desc limit 20
--------------------------------
select key2,count(*) as num_2
from
dw.table_b
group by key2
order by num_2 desc limit 20
---------------------------------
比如说,总共有
100 万个 key 。只有 2 个 key ,是数据量达到 10 万的。其他所有的 key ,对应的数量都是几十,这样 join 后会引起倾斜。这个时候,自
己可以去取舍,如果业务和需求可以理解和接受的话,在从 hive 表查询源数据的时候,直接在 sql 中 用 where 条件,过滤掉某几个 key 。那么这几个
原先有大量数据,会导致数据倾斜的 key ,被过滤掉之后,那么在的 spark 作业中,自然就不会发生数据倾斜了。
解决方法
2 Hive ETL 做处理
导致数据倾斜的是
Hive 表。如果该 Hive 表中的数据本身很不均匀(比如某个 key 对应了 100 万数据,其他 key 才对应了 10 条数据),而且应用中需
要频繁使用 Spark 对 Hive 表执行分析操作时,可以使用 Hive ETL 去做一个预处理
实现方式
通过
Hive ETL 预先对数据按照 key 进行聚合,或者是预先和其他表进行 join ,然后在 Spark 作业中针对的数据源就不是原来的 Hive 表了,而是预处
理后的 Hive 表。此时由于数据已经预先进行过聚合或 join 操作了,那么在 Spark 作业中也就不需要使用原先的 shuffle 类算子执行这类操作了。 Hive
ETL 中进行 group by 或者 join 等 shuffle 操作时,还是会出现数据倾斜,导致 Hive ETL 的速度很慢。我们只是把数据倾斜的发生提前到了 Hive ETL 中
解决方法
3 :提高 shuffle 操作并行度
在对RDD 执行 shuffle 算子时,给 shuffle 算子传入一个参数,比如 reduceByKey ( 1000),该参数就设置了这个 shuffle 算子执行时 shuffle read task的数量。对于 Spark SQL 中的 shuffle 类语句,比如 group by 、 join 等,需要设置一个参数,即 spark.sql.shuffle.partitions ,该参数代表了 shuffle read task 的并行度,该值默认是 200 ,对于很多场景来说都有点过小
原理:
增加 shuffle read task 的数量,可以 让原本分配给一个 task 的多个 key 分配给多个 task 从而让每个 task 处理比原来更少的数据 。举例来说,
如果原本有 5 个 key ,每个 key 对应 10 条数据,这 5 个 key 都是分配给一个 task 的,那么这个 task 就要处理 50 条数据。而增加了 shuffle read task 以后,每个 task 就分配到一个 key ,即每个 task 就处理 10 条数据,那么自然每个 task 的执行时间都会变短了

合并小文件

hadoop fs ls / 文件地址 可以查看 Hive 表中每个数据文件的大小。小文件一般都几 k 、几十 k 的。

HDFS 用于存储大数据的文件,如果 Hive 中存在过多的小文件会给 namenode 带来较大的性能压力。同时小文件过多时会影响 spark 中 job 的
执行。为了提高 namenode 的使用效率,在向 hdfs 加载文件时需要提前对小文件进行合并;

Spark 将 job 转换成多个 task ,从 hive 中拉取数据,对于每个小文件也要分配一个 task 去处理,每个 task 只处理很少的数据,这样会起上万个
task ,非常影响性能

处理大量小文件的速度远远小于处理同样大小的大文件速度, Task 启动将耗费大量时间在启动 task 和释放 task 上。
为了防止生成小文件,在hive ETL 的时候可以通过配置参数在 MapReduce 过程中合并小文件。
一般在对ods 层日志数据进行处理时,如果小文件过多,需要重新 ETL 合并小文件再重新写入
输出合并:
合并输出小文件,以减少输出文件的大小,可通过如下参数设置:
set hive.merge.mapfiles =true; // map only job 结束时合并小文件
set hive.merge.mapredfiles =true; // 合并 reduce 输出的小文件
set hive.merge.size.per.task =64000000; 合并之后的每个文件大小 64M