【spark】关于spark处理上百T数据清洗过滤的思考

我,菜鸡一只!

本文会通过读取数据文件,外部传入参数,处理数据,保存数据,参数设置这几个点来宏观的说说我自己对于spark使用中的一些注意点

继上一次写文章到现在好久了哦!

工作上,数据的日常需求还是一直有的,然后我自己又想接一接java功能上的需求(多写写java代码提升自己的眼界),在这样的情况下,我的工作已经基本饱和了,结果突然领导说还要开个新的数据模型,年前要给测试反馈,所以12月后,我就一直没有更新文章(其实也有一定原因是因为快过年了,有点松懈!)

后来又由于这个疫情,放假在家办公,恢复了数据,支持了一些部门之后,稍微闲下来写写博客!

本文,其实算是这段时间内通过spark处理数据的经验总结和杂谈吧~大家有兴趣可以看看!

1、读取数据文件

在读取数据文件的时候,既要采用批量读取,又要考虑资源问题

对于很大的数据集,往往不会存成单独的文件块,一般是会有维度的,例如:日期,省份,时间等,spark在读取数据的时候要考虑如何写读取数据的路径

例:数据在hdfs上路径:

/files/日期/小时/part-0000

                       /part-0001

                       xxxxx

如果要读取某一天某一个小时的所有数据:spark.read.textFile('/files/日期/小时')

通过这种指定目录的方式,读取目录下所有文件,肯定比一个一个指定文件名称读取的快

注:但是也不能一次性指定最大的路径,首先API是否能递归读到文件,我未测试过,其次,假设中间某个文件出问题,导致spark任务挂了,也会使得之前所有正确的文件没有保存下来

 

2、外部传入参数

在spark任务提交的时候写 --conf spark.external.parameter=10

然后就可以在代码中,通过spark.conf.get("spark.external.parameter")来获取自定义的这个参数,大家注意下get和spark.conf.getOption的区别就好,其他没什么要说的

 

3、处理数据

处理数据,要尽量的考虑,减少DAG,减少shuffle,实在不行也要减少shuffle的数据量,一个API能完成的事情,最好不要使用两个API来完成

例子1:

我想实现2个功能点,第一过滤不要的数据,二是数据中部分字段想做处理,部分想丢弃

那么有3种API可以实现:

-1.先filter过滤数据再map,将数据转换成想要的格式

-2.通过mappartition一次性将过滤和格式转换处理完成

-3.通过flatmap来处理数据

三种方式都能解决问题,但是第一种要走两个API,因此性能上不是最好的,第二种会一次性将整个分区的数据读入再进行处理,容易引发OOM(小心为妙),第三种flatmap可能会有些同学没有想到,这也可以?贴上一小段代码,大家就知道了

Dataset<Row> baseFilters =  stringDataset.flatMap(new FlatMapFunction<String, BaseFilter>() {
                    @Override
                    public Iterator<BaseFilter> call(String s) throws Exception {
                        //解析读入的字符串
                        BaseFilter baseFilter = parseRow(s);
                        if (数据合法) {
                            return Arrays.asList(baseFilter).iterator();
                        }else {
                            return Collections.emptyIterator();
                        }
                    }
                }, Encoders.bean(BaseFilter.class)).toDF()

flatMap的功能很强大,大家可以自行百度,通过flatmap可以将数据转换类型,并且过滤,甚至一条数据可以变多条(因为api可以return一个iterator)

例子2:

当遇到join的操作的时候,如果这个join只做过滤用,那么建议可以不使用join(因为join会触发shuffle,会大大延长spark任务的时间),将小数据集做成HashMap(做成什么数据类型,还要考虑数据结构,hashMap取数据很快)放到广播变量中,然后map大数据集的时候,过滤掉不符合的数据

如果实在要join,那就要多思考有没有数据倾斜的问题,是否可以从大表中,就拿出join的关键字段,减少join的数据量,join完之后,再拼接会大表中,当然提供的这些方法还需要结合自身实际场景使用

例子3:

能不用窗口函数就不要用,如果真要用,可以看看我以前遇到的场景和坑

【spark】记录spark使用窗口函数的一次问题:https://blog.csdn.net/lsr40/article/details/87188072

 

4、保存数据

很早以前,我写过一个系列(也就2篇文章),总结了如何让spark可以自动判断合理分块数量

如下有跳转链接:

该系列暂有2篇文章(本文为第2篇):

【spark】存储数据到hdfs,自动判断合理分块数量(repartition和coalesce)(一):https://blog.csdn.net/lsr40/article/details/84968923

【spark】存储数据到hdfs,自动判断合理分块数量(repartition和coalesce)(二):

https://blog.csdn.net/lsr40/article/details/85078499
 

对于这两篇文章,我有新的理解:

我在文中有提到,方法二:保存数据之前判断(抽样),我来大概解释下,原理是这样:取前100条数据,判断100条数据占用的空间大小,然后count整个数据集,通过比例放大,以此来估计我将要保存的数据的大小!

讲实话,这是一个我觉得很不错的办法,适用于各种各样的场景之中!

方法二的个人体会:

优点:

写好代码之后,从此要计算任何数据我都不需要考虑保存等相关问题, 调用保存方法,自动处理小文件问题

缺点:

1、数据集的条数,可以直接通过简单的count得到,或者自己定义一个spark累加器来得到,但是不管用哪一种方式,在某些极端情况下(服务器失去连接不上,container丢失,数据倾斜等问题),count值会有偏离,有时候还偏离蛮多的,所以通过这种方式打印出来的count值的准确性有待商榷,不可直接用作监控(不过如果用作估计文件repartition个数的话,那完全没问题),建议监控还是另写一个程序,在保存完之后,对数据集进行count,用此值来做监控

2、由于多了一个count步骤,对于数据量很大,十几个T甚至上百个T的数据,无疑是一个比较大的消耗(因为数据要先cache,然后再count和save),会导致本来1个小时能跑完的任务,强行跑了1个半小时甚至更多,在很多要求时间的任务上,这种方案显得不是那么的好

注:spark当中还有一个sample这样的抽样api,这个抽样是给定百分比的,理论上我可以抽百分之5的数据,然后反推数据大小,数据条数啊,这个sampleAPI我没有做更多的测试,所以我没有把握说这个算子的性能到底有多好,就算有提升,但是总归还是要将要保存的结果数据cache,然后抽样,然后计算抽样的count,再保存,还是躲不开多余的步骤

 

方法一:人工判断,(适合场景:过滤日志)

在之前的文章,我并没有对方法一有太详尽的说明:

最近在做上百T数据的过滤清洗,我发现方法二在保存数据以G为单位的情况下(当然是以我们集群的资源来说的),还能接受,但是一旦数据单位上升到T了之后,会大大的延长清洗数据的时间,因此我不想在保存之前多这么一个count步骤。

我采取的方案如下,就是先拿到读入数据的一个大概估值,然后再根据我清洗会过滤多少数据百分比来得到保存的数据大小,直接保存数据,不采取其他多余的操作

获取原始数据的大小方式应该有很多,我这里随便说两种(数据在HDFS上)

1、hdfs上每个数据文件块大小从80M~250M不等,大概在这个量级内,spark.read.textFile得到的Dataset中有一个API叫做inputFiles(),可以获得这个Dataset对应的数据块的路径名称,这样就可以知道本次读入几个文件块,然后计算出一个大概的文件大小,用inputFiles()API要注意,当你做了cache或者其他的一些转换操作,有可能就会丢失inputFiles信息,所以使用前请多测试

2、hdfs有个命令,hdfs dfs -count /目录 ,会打印出文件块个数和大小,也可以通过一条shell(可以在代码中执行shell,也可以当做外部参数传入)来获取文件块个数和原始数据大小(单位我记得是字节,大家自己转换下就好)

 

4、资源参数的设置

这一直是一个很头疼的问题

我们大多数都是把任务提交到yarn上运行的,所以我就说yarn(其他的我也没有太多的经验)

曾经看过一个hadoop团队的人写了一篇博客来说这个资源的事情,具体我可能找不到了

他的意思是说,每个executor需要给定5个以内的核心,给太多核心会相互抢占资源出bug,但是也不能只给1,2个,因为给太少,就发挥不了多线程跑task(特别是广播变量,如果每个executor就一个核心,那广播变量就毫无优化可言了,因为广播变量是每个executor一份,executor中所有的core共享),因此我基本上在跑spark任务的时候,给定的核心数为4或者3

那我就根据这个值,来计算我的资源够开几个executor,然后把合适的内存分给executor就ok了,因此我可能会使用如下参数
 

--num-executors 100 \
--executor-memory 16G \
--driver-memory 3G \
--executor-cores 4  \

当然如果你使用我的参数,遇到了OOM,那么请考虑下,是数据倾斜吗,是否能从代码层面解决?

实在不行,可以提高executor-memory(内存)或者降低executor-cores(核心数/并行度),来使任务继续运行下去!

当然我的参数也不是万能的,只是对应我的应用场景和集群资源罢了,希望我给你的思路,可以让你找到适合你们公司或者你们业务的参数列表
 

注:关于写好的资源,肯定会和实际申请的资源不一样,这是因为spark有一些额外申请的空间,如果真的很想知道的话这部分怎么产生的,可以百度几篇博客看看spark提交到yarn上资源申请的源码(我当年也是看过的,但是基本忘光了,而且我个人觉得,相对来说这比较不那么重要)

 

本人菜鸡一只,也正在学习中,如果我有什么写的不对或者不清晰的地方,希望大家指出!

这是2020年的第一篇文章~2020年中国加油,武汉加油!!(还有我自己也要加油~)

题外话:《庆余年》还挺好看的,搞得我都想买书来看后续了,哎再忍忍吧,最近在看《代码整洁之道》,推荐没看过的同学还是要买来看看的,我看了才知道,自己以前写的代码。。。真是一言难尽!!!

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值