我们先从下面最简单的Demo入手,一步一步迭代
// val time = sc.getConf.get("spark.time", "")
val time = "2019-06-08 10:14:09"
val parsedTime = DateUtils.parseToMinute(time)
val day = DateUtils.getDay(parsedTime) // "20190608"
val hour = DateUtils.getHour(parsedTime) // "10"
val inputPath = s"hdfs://hadoop004:9000/input/$day/$hour/*"
val outputPath = s"hdfs://hadoop004:9000/output/$day/$hour/"
val appName = "EtlOffline"
val start = System.currentTimeMillis()
val totals = sc.longAccumulator("totals")
val errors = sc.longAccumulator("errors")
val rddText = sc.textFile(inputPath)
if (rddText.isEmpty) {
// logError(s"\n------ The file is empty ------\n")
System.exit(1)
}
val etlDf = spark.createDataFrame(
rddText.map(x => {
EmpParser.parseLog(x, totals, errors)
}).filter(_.length != 1),
EmpParser.struct
)
etlDf
.write.mode(SaveMode.Append)
.save(outputPath)
etlDf.show(50, false)
// 计算总的条数还有错误的条数
val totalValues = totals.count
val errorValues = errors.count
val timeDuration = (System.currentTimeMillis() - start) / 1000
// logInfo(s"\n------ $appName App 执行成功,耗时:${timeDuration}秒, 总记录数:$totalValues, 错误数:$errorValues ------\n")
查看输出文件
查看文件内容
从上面两个输出结果可以看到,/input/20190608/10/目录下有两个文件,写出去的时候分别输出两个文件各自对应
上面同一份代码再次运行,由于我的savemode是append,因此输出结果会再次生成两个文件
从上面的演示结果来看,2019060810的数据中可能有9点的数据,一开始,先把time拿出来,计算出day和hour,然后再把2019060810里面的数据保存,但这样的话,输出的文件包含有9点和10点的数据,而且使用append模式的话,当我重跑的时候,数据重复了
接着,我们直接把savemode改为overwrite看看效果,代码如下
查看输出结果
可以看到,使用了overwrite之后,原先/output/20190608/10/下面的四个parquet文件现在只剩下两个了,还记得之前是append了两次,原本是有4个输出文件的,但现在只剩下两个了,也就是说使用了overwrite会直接把目录下的文件全都干掉,先记住这一点,后面会用到
然而到现在,问题还没有解决,output中10的文件还是残留有9点的数据,如下图
继续,如果把数据groupby分组之后再保存如何?效果一样,groupby只是简单分组,不一定按文件输出
解决方案,partitionBy,代码如下
查看输出结果,可以看到/output/20190608/10/day=20190608目录下有hour=09和hour=10两个目录
查看两个目录下的parquet文件
下面的结果来自1.log中10点的数据
下面的结果来自2.log中10点的数据
可以看到,10点的数据输出到10点的文件,同理,9点的数据输出到9点的文件
修改代码,跑9点批次的数据
查看输出文件
10点的目录被干掉了,怎么办,如果使用append的话,那应该是没有问题的,但是如果重跑,数据就会有重复。如果是8号的数据中还有7号的数据,那么再跑8号的数据的时候会直接把昨天跑的数据直接干掉
直接看解决方案
10点跑9点的数据,9点的数据中除了9点的数据,还有几条8点的数据
output/2019060809/day=20190608/hour=09/part-00000-4703aa07-7e7e-4753-ac9e-9ec634ff74f5.c000.snappy.parquet
output/2019060809/day=20190608/hour=09/part-00001-4703aa07-7e7e-4753-ac9e-9ec634ff74f5.c000.snappy.parquet
output/2019060809/day=20190608/hour=08/part-00000-4703aa07-7e7e-4753-ac9e-9ec634ff74f5.c000.snappy.parquet
output/2019060809/day=20190608/hour=08/part-00001-4703aa07-7e7e-4753-ac9e-9ec634ff74f5.c000.snappy.parquet
rename
output/day=20190608/hour=08/2019060809-part-00000.snappy.parquet
output/day=20190608/hour=08/2019060809-part-00001.snappy.parquet
output/day=20190608/hour=09/2019060809-part-00000.snappy.parquet
output/day=20190608/hour=09/2019060809-part-00001.snappy.parquet
11点跑10点的数据,10点的数据中除了10点的数据,还有几条9点的数据
output/2019060810/day=20190608/hour=09/part-00000-4703aa07-7e7e-4753-ac9e-9ec634ff74f5.c000.snappy.parquet
output/2019060810/day=20190608/hour=09/part-00001-4703aa07-7e7e-4753-ac9e-9ec634ff74f5.c000.snappy.parquet
output/2019060810/day=20190608/hour=10/part-00000-4703aa07-7e7e-4753-ac9e-9ec634ff74f5.c000.snappy.parquet
output/2019060810/day=20190608/hour=10/part-00001-4703aa07-7e7e-4753-ac9e-9ec634ff74f5.c000.snappy.parquet
rename
output/day=20190608/hour=10/2019060810-part-00000.snappy.parquet
output/day=20190608/hour=10/2019060810-part-00001.snappy.parquet
output/day=20190608/hour=09/2019060810-part-00000.snappy.parquet
output/day=20190608/hour=09/2019060810-part-00001.snappy.parquet
此时output/day=20190608/hour=09中的数据如下
output/day=20190608/hour=09/2019060809-part-00000.snappy.parquet
output/day=20190608/hour=09/2019060809-part-00001.snappy.parquet
output/day=20190608/hour=09/2019060810-part-00000.snappy.parquet
output/day=20190608/hour=09/2019060810-part-00001.snappy.parquet
现在12点再次重跑10点的数据,先把output/day=20190608/hour=09目录下2019060810批次的数据删掉
再把output/day=20190608/hour=10目录下2019060810批次的数据删掉
然后再重跑10点的数据,跑完之后再rename
测试reRunDelFiles和opFiles两个函数
reRunDelFiles(fs, "/test", "2019060810"),这个函数的作用是如果重跑,先把该批次的输出文件删掉
[hadoop@hadoop004 ~]$ hdfs dfs -ls /test/day=20190608/hour=10
Found 1 items
-rw-r--r-- 1 hadoop supergroup 1003 2019-08-06 12:18 /test/day=20190608/hour=10/2019060810-00001.snappy.parquet
[hadoop@hadoop004 ~]$ hdfs dfs -ls /test/2019060810/day=20190608/hour=09
Found 1 items
-rw-r--r-- 1 hadoop supergroup 1003 2019-08-06 17:58 /test/2019060810/day=20190608/hour=09/2019060810-00001.snappy.parquet
测试opFiles函数
测试效果:
第一次运行,创建正式目录 output/day=20190608/hour=10 output/2019060810/day=20190608/hour=10/* --> output/day=20190608/hour=10/*
第二次运行,不用创建正式目录,仅仅挪文件
[hadoop@hadoop004 ~]$ hdfs dfs -ls /test/2019060810/day=20190608/hour=10
Found 2 items
-rw-r--r-- 1 hadoop supergroup 1003 2019-08-06 18:40 /test/2019060810/day=20190608/hour=10/2019060810-00001.snappy.parquet
-rw-r--r-- 1 hadoop supergroup 1003 2019-08-06 18:41 /test/2019060810/day=20190608/hour=10/2019060810-00002.snappy.parquet
[hadoop@hadoop004 ~]$ hdfs dfs -cp /tmp/2019060810-00001.snappy.parquet /test/2019060810/day=20190608/hour=09
[hadoop@hadoop004 ~]$ hdfs dfs -ls /test/2019060810/day=20190608/hour=09
Found 1 items
-rw-r--r-- 1 hadoop supergroup 1003 2019-08-06 18:42 /test/2019060810/day=20190608/hour=09/2019060810-00001.snappy.parquet
单独测试opFiles函数
整个流程跑通,先跑10点的数据,得到如下结果
然后跑9点的数据
重跑10点的数据
成功!!