在对数据进行处理的时候,很多时候需要我们进行数据清洗。
下面的案例就是对大量的数据进行处理:
每行代码完成的任务在备注中都有叙述
package etl
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import bean.{
Logs, logSchema}
import config.ConfigHelper
import org.apache.commons.io.FileUtils
import org.apache.spark.rdd.RDD
import org.apache.spark.sql
import org.apache.spark.sql.{
Row, SaveMode, SparkSession}
/**
* 1、清洗
* 2、转换
* */
object log2Parquet {
def main(args: Array[String]): Unit = {
//会有两个参数,一个是输入路径,一个是输出路径,因此参数<2的话说明有参数丢失,报错提醒我们检查
if(args.length<2){
println("参数错误")
return
}
//需要创建一个SparkSession
val session=SparkSession
//创建
.builder()
//设置是否是在本地运行
//*代表几个线程
.master("local[*]")
//设置进程的名称
.appName(this.getClass.getName)
//压缩
.config("spark.sql.parquet.compression.codec",ConfigHelper.parquetCode)
//序列化
.config("spark.serializer",ConfigHelper.serializer)
//获取(如果有就拿原来的,没有就创建)
.getOrCreate()
//导入隐式转换
import session.implicits._
//读取数据
val sources: RDD[String] = session.sparkContext.textFile(args(0))
//切分数据,判断数据长度(字段的数量)是否大于55
//因为设置了55个字段,如果切割以后字段数量小于55,说明一定在切分过程中出现了问题
//limit
//什么都不加:300T// 剪切完:300T
//加-1:300T// 剪切完:300T “” “” “” “” “” “”
//filter效率很低,尽量少用
//rdd复用提高效率:
//缓存机制:不是安全的,缓存之前的血统关系
//缓存分为cache和persist
//cache:底层调用persist,传给内存
//persist的缓存级别:十二种
//分为:内存,内存序列化,内存磁盘,内存磁盘序列化,磁盘,堆外内存 X2
// 检查点机制:将中间的数据,存到一个可靠的文件系统里,比如HDFS(数据是安全的,会切断之前的血统关系)
//spilt中的regax属性里面的"\\"是转译符号,除非后面的符号是","或者";",不然类似于"|"这样的前面都要加上"\\"
val filted = sources.map(line=>line.split("\\|",line.length)).filter(_.length>=55)
//挨个处理300T和300TATO
//获取当前时间和数据时间
val format = new SimpleDateFormat("yyyyMMddHHmmss")
val date = new Date()
val currentTime: Long = date.getTime
//清洗300T和300TATO
//四道筛选都满足的数据才能留下来
//第一道筛选:第一个属性不包含300T和300TATO的数据剔除
val rdd300T = filted.filter(arr=> arr(0).contains("300T") || arr(0).contains("300TATO"))
//第二道筛选:剔除第18个属性里包含复位、SB待机和常用制动报警的数据
.filter(arr => !arr(17).contains("复位") || !arr(17).contains("SB待机") || !arr(17).contains("常用制动报警"))
//第三道筛选:第八个属性是入库时间与数据时间相差的时间,超过一定时间的数据剔除
//currentTime:现在的时间
//(format.parse(arr(7))).getTime:arr(7)是数据入库时间,经过format.parse(),转换成我们之前设定的时间格式,最后加上.getTime就和currentTime的格式一样了,是以毫秒为单位的
//5 * 365 * 24 * 60 * 60 * 1000:5年
//5:5年
//365:一年有365天
//24:一天有24小时
//60:一小时有60分钟
//60:一分钟有60秒
//1000:因为时间最后的单位是毫秒,而秒和毫秒的进制是1000,所以最后乘上1000
.filter(arr => (currentTime - format.parse(arr(7)).getTime < 5 * 365 * 24 * 60 * 60 * 1000))
//第四道筛选:剔除第18个属性里面包含休眠、未知、无致命错误、一致性消息错误、NVMEN故障的数据
.filter(arr => !arr(17).contains("休眠") || !arr(17).contains("未知") || !arr(17).contains("无致命错误")
|| !arr(17).contains("一致性消息错误") || !arr(17).contains("NVMEN故障"))
//挑出来当前ATP系统处于故障中
//distinct:去重
val rdd300TSF = rdd300T.filter(arr=>arr(17).contains("前ATP系统处于故障中"))
.map(arr=>{
arr(0)+"|"+
arr(1)+"|"+
arr(2)+"|"+
arr(3)+"|"+
arr(4)+"|"+
arr(5)+"|"+
arr(6)+"|"+
//arr(7):20150109102323