一、大数据的相关基础知识
(一)定义
大数据是指无法在有限时间内用常规软件工具对其进行获取、存储、管理和处理的数据集合。
(二)特征4V
1.Volume---数据体量巨大
数据已从TB级增加到PB级。
2.Velocity---数据产生、处理和分析的速度在持续加快
数据产生具有实时性。
3.Variety---数据类型繁多
现在数据类型不再是单一的格式化数据,更多的是半结构化或非结构化数据,如博客、即时消息、视频、音乐、照片、点击流、日志文件等。
4.Value---大数据的数据价值密度低
单位数据的价值密度不断降低,以监控视频为例,在一小时的视频中,有用的数据可能仅仅只有一两秒,但这可能是解决问题的关键。
(三)大数据的来源
1.运营式系统阶段
这个阶段最主要的特点是数据的产生往往伴随着一定的运营活动,数据是记录在数据库中的。
2.用户原创内容阶段
Web2.0最重要的标志是用户原创内容,以微博、微信、抖音等为代表的新型网络的app的兴起。
3.感知式系统阶段
数据来源包括微小且带有处理功能的传感器(AI摄像头、传感器等)。
(四)大数据的处理过程
1.数据收集(采集)
数据收集即获取数据的过程。
(1)网络爬虫程序---web数据
(2)通过数据库接口完成数据读取---数据库数据
(3)通过服务日志等获取相关信息---各种Service服务器数据
2.数据预处理
主要包括数据清理、数据集成、数据规约与数据转换等内容
(1)数据清理---对数据的不一致检测、噪声数据的识别、数据的过滤与修正等。
(2)数据集成---将多个数据源的数据进行集成,从而形成集中、统一的数据库和数据仓库等。
(3)数据规约---在不损害分析结果准确性的前提下降低数据集规模,使之简化,包括维度规约、数量规约、数据压缩等技术
(4)数据转换---包括基于规则或元数据的转换、基于模型与学习的转换等技术,可通过转换实现数据统一
3.数据处理与分析
计算模型主要有MapReduce分布式计算框架、Spark分布式内存计算系统、Filink分布式流计算系统等
(1)MapReduce---批处理的分布式计算框架,磁盘的IO开销较大
(2)Spark---基于内存计算,执行效率较高
(3)Flink---对数据流进行实时处理
4.数据可视化及应用
(1)数据可视化
将大数据分析与预测的结果以图形、图像等方式直观地显示给用户,并可与用户进行交互
(2)大数据应用
将经过分析处理后挖掘得到的大数据结果应用于管理决策、战略规划等的过程。
(五)Hadoop生态圈
Hadoop是由Apache基金会开发。
Hadoop生态体系主要包括HDFS、MapReduce、Hbase、Zookeeper、Hive等核心组件构成,还包括Flume、Kafka等框架
二、Spark相关基础知识
(一)Spark生态圈
1.Spark Core:将分布式数据抽象为弹性分布式数据集(RDD)进行处理。
2.Spark SQL:主要负责处理结构化数据,它支持对Hive表、parquet、json及数据库等多种数据源。
3.Spark Streaming和Structed Streaming:针对实时数据进行流式处理。
4.GraphX---分布式图计算框架,能高效进行图计算。
(二)Spark的特点
1.高效性:
与hadoop相比,运行速度提高了100倍,基于内存的计算,内存的读写效率较高。
2.易用性:
支持Scala、Java、Python、R等语言,还支持超过上百种高级算法。
3.通用性:
可用于批处理、交互式查询、实时流处理、机器学习、图计算等场景。
4.兼容性:
spark可以非常方便地与其他开源产品进行融合。
三、Scala的简单语法知识
(一)Scala简介
1.Scala是面向对象语言
2.Scala是函数式编程语言
3.Scala是静态类型的
4.Scala是可扩展的
(二)Scala安装
在安装Scala之前,需要下载、安装并配置好JDK环境
(三)Scala基础语法
1.常量和变量的定义
(1)val---定义常量,不可变
(2)var---定义变量,可变
2.匿名函数
所谓匿名函数,就是没有名字的函数,即定义函数时省略函数名称。函数名称使用"=>"来定义,等号左边为函数的参数列表,箭头右边为函数主题(所要实现的功能)。
可以把匿名函数给一个变量,然后通过变量名调用该匿名函数
3.字符串的处理
(1)length()---返回字符串的长度
(2)equals()---判断两个字符串是否相等
(3)startsWith()/endsWith()---判断字符串是否以指定字符串开头/结尾
(4)repalce()---将指定字符串替换为新的字符串
(5)contains()---判断字符串是否包含指定字符串
(6)substring(start,end)---字符串截取,从指定的下标开始和结束索引,范围是左闭右开
(7)split()---字符串的切割,按照指定的分隔符对原字符串进行切割,得到一个字符串数组
(8)trim()---去除字符串首尾的空白字符
4.元组
元组(Tuple)是大数据处理中常用的数据类型,它可以包含不同类型的数据元素,使用小括号将这些数据元素括起来,并用逗号隔开
【获取元素的值】
使用脚注来获取元素的值。
例如:tuple._1获取元组的第一个元素;tuple.__2获取元组的第二个元素
5.高阶函数
(1)map操作
针对集合元素的变换操作。
(2)filter操作
遍历集合中的所有元素,筛选出符号某特定条件的元素。
(3)foreach操作
与map类似,也是对集合中的每一个元素进行操作,但foreach没有返回值。
(4)reduce操作
对列表中的元素进行合并、累计。
四、Spark RDD
(一)初始RDD
RDD---弹性分布式数据集
(二)创建RDD
1.由程序中的数据集合创建RDD---parallelize/makeRDD
这两个方法都包含两个参数,第一个参数:Seq集合名称;第二个参数:分区数量
注意:
获取rdd分区数量:rdd.partitions.size
2.由外部存储文件创建RDD---textFile
注意:
文件存储在HDFS上/spark/data下的时候地址应该怎么写?hdfs://主机名或IP地址:8020/spark/data
(三)RDD操作
1.转换操作:
(1)map
(2)filter
scala
/**
* map---对rdd数据集中的每个元素都做相同的转换操作
* 操作以匿名函数的形式的书写
* 参数列表=>函数体
*/
//intRdd中的每个元素都+1
val rddAdd1 = intRdd.map(x=>x+1)
println("+1转换后为:")
rddAdd1.foreach(println)
println("-------------------------")
val strlist = List("heLLO","spArk","haDoop")
val strRdd = sc.parallelize(strlist)
val upperRdd = strRdd.map(x=>{
val tou = x.substring(0,1).toUpperCase()
val wei = x.substring(1,x.length).toLowerCase()
tou+wei
})
upperRdd.foreach(println)
println("-------------------------")
val tuplelist = List(("Tom",18),("Bob",20),("Jack",17))
val tupleRdd = sc.parallelize(tuplelist)
/**
* 对于元组中元素的访问
* ._1---访问元组中的第一个元素
* ._2---访问元组中的第二个元素
*/
//("Tom",19,"Tom@huawei.com)
val newRdd = tuplelist
.map(x=>(x._1,x._2+1,x._1+"@huawei.com"))
newRdd.foreach(println)
println("------------------------")
//flatMap:包含map的转换逻辑,加入了扁平化转开操作
val list2 = List(Array(1,2,3),Array(4,5),Array(6))
val rdd2 = sc.parallelize(list2)
val flatRdd = rdd2.flatMap(x=>x)
flatRdd.foreach(println)
val strlist2 = List("hello java","hello hadoop","hello spark")
val strRdd2 = sc.parallelize(strlist2)
val mapRdd = strRdd2.map(x=>x.split(" "))
val fmapRdd = strRdd2.flatMap(x=>x.split(" "))
mapRdd.foreach(println)
println("------------------------------")
fmapRdd.foreach(println)
(3)groupBy
scala
val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9,10))
//筛选rdd1中的偶数
val rdd2 = rdd1.filter(x=>x%2==0)
rdd2.foreach(println)
val rdd3 = sc.parallelize(List(("Tom",73),("Bob",56),("Jack",88)))
//筛选出成绩及格的学生的数据
val rdd4 = rdd3.filter(x=>x._2>=60)
rdd4.foreach(println)
//合并---union(不会去重)
val rdd5 = sc.parallelize(List(1,3,5,7,9,2))
val rdd6 = sc.parallelize(List(2,4,6,8,10,2))
val rdd7 = rdd5.union(rdd6)
println("---------------------------")
rdd7.foreach(println)
//交集---intersection相同的对元素
val rdd8 = rdd5.intersection(rdd6)
println("---------------------------")
rdd8.foreach(println)
//差集---sub
val rdd9 = rdd5.subtract(rdd6)
println("---------------------------")
rdd9.foreach(println)
val rdd10 = rdd6.subtract(rdd5)
println("---------------------------")
rdd10.foreach(println)
//去重
val rdd11 = rdd6.distinct()
println("---------------------------")
rdd11.foreach(println)
(4)sortBy max min mean
scala
val rdd1 = sc.parallelize(List(4,1,6,2,7,3,5))
val sortRdd1 = rdd1.sortBy(x=>x,false)
sortRdd1.foreach(println)
//max min mean 对数值型元素求最值 和 平均值
val maxRdd = sortRdd1.max()
val minRdd = sortRdd1.min()
val meanRdd = sortRdd1.mean()
println("最大值:"+maxRdd)
println("最小值:"+minRdd)
println("平均值:"+meanRdd)
val rdd2 = sc.parallelize(List(("Tom",78),("Bob",83),("Jerry",65)))
val sortRdd2 = rdd2.sortBy(x => x._2, false)
sortRdd2.foreach(println)
//姓名:Tom 分数;78
sortRdd2.foreach(x=>println("姓名:"+x._1+"\t分数:"+x._2))
val rdd3 = sc.parallelize(List(Array(18,78),Array(23,83),Array(34,65)))
val sortRdd3 = rdd3.sortBy(x=>x(1),false)
sortRdd3.foreach(println)
(5)keys/values
(6)lookup
(7) groupByKey
(8)reduceByKey
(9)sortByKey
(10)mapValues
scala
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.SparkSession
object RddDemo6 {
def main(args: Array[String]): Unit = {
//1.设置日志级别
Logger.getLogger("org").setLevel(Level.OFF)
//2.创建SparkSession对象
val spark = SparkSession
.builder()
.appName("rdd5")
.master("local")
.getOrCreate()
//3.SparkContext对象的获取
val sc = spark.sparkContext
/*
键值对RDD(分布式弹性数据集)的创建和操作
一、 键值对RDD(分布式弹性数据集)的创建
键值对(map集合) (键,值) 键和值之间有对应关系
---二元组(包含两个元素的元组)
1.基于内存中已经存在的数据创建(List Array)
2.对普通的Rdd转换为键值对RDD
3.使用zip函数将两个RDD转换为键值对RDD
*/
//1.基于内存中已经存在的数据创建(List Array)
val list = List(("Tom",82),("Bob",76),("Jack",92))
val rdd1 = sc.parallelize(list)
// 2.对普通的Rdd转换为键值对RDD
val rdd2 = sc.parallelize(List("hello","java","spark"))
//(单词,单词的长度)
val rdd3 = rdd2.map(x=>(x,x.length))
rdd3.foreach(println)
//3.使用zip函数(拉链函数)将两个RDD转换为键值对RDD
val rdd4 = sc.parallelize(List("东岳","西岳","南岳","北岳","中岳"))
val rdd5 = sc.parallelize(List("泰山","华山","衡山","恒山","嵩山"))
val rdd6 = rdd4.zip(rdd5)
println("---------------------------------")
rdd6.foreach(println)
/*
二、 键值对RDD(分布式弹性数据集)的操作
1.keys values
2.lookup
3.byKey
4.mapValues
*/
/*
1.keys---获取所有键值对的key
2.values---获取所有键值对的value
*/
val rdd7 = rdd6.keys
println("---------------------------------")
rdd7.foreach(println)
val rdd8 = rdd6.values
println("---------------------------------")
rdd8.foreach(println)
/*
2.lookup---查找某个key对应的value
*/
val rdd9 = rdd6.lookup("东岳")
println("---------------------------------")
rdd9.foreach(println)
/*
ByKey操作
(1)groupByKey ---按照key进行分组
CompactBuffer---spark数据结构(可变数组)
(2)reduceByKey---按照key进行分组后,对同一个组中的多个value值做计算操作
(3)sortByKey---按照key进行排序
*/
//(1)groupByKey ---按照key进行分组
val rdd10 = sc.parallelize(List(("水果","apple"),("水果","banana"),("蔬菜","白菜"),("蔬菜","黄瓜")))
val rdd11 = rdd10.groupByKey()
println("---------------------------------")
rdd11.foreach(println)
//(3)sortByKey---按照key进行排序:默认是升序,如果需要降序
val rdd12 = sc.parallelize(List((3,"Tom"),(1,"Bob"),(2,"Jack")))
val rdd13 = rdd12.sortByKey(false)
println("---------------------------------")
rdd13.foreach(println)
/*
(2)reduceByKey---按照key进行分组后,对同一个组中的多个value值做计算操作
("banana",[3,6,2])
("apple",[5,4,3])
(a,b)=>a+b (接收实际参与运算的数据(实参))参数列表=>函数体(计算操作)
第一次计算:
拿出多个value值的任意2个
之后,a中保存的都是前一次计算的结果,b去接受新的value值
*/
val rdd14 = sc.parallelize(List(("banana",3),("apple",5),("banana",6),("apple",4),("banana",2),("apple",3)))
val rdd15 = rdd14.reduceByKey((a,b)=>a+b)
println("---------------------------------")
rdd15.foreach(println)
//4.mapValues---对键值对RDD中的value进行处理
val rdd16 = sc.parallelize(List(("banana",5),("apple",4)))
val rdd17 = rdd16.map(x=>(x._1,x._2*0.6))
val rdd18 = rdd16.mapValues(x=>x*0.6)
println("---------------------------------")
rdd18.foreach(println)
}
}
2.行动操作
(1)take
(2)collect
(3)first
scala
/**
* RDD的简单操作
* 行动操作---
* take(n)---获取当前rdd数据集中的前n个元素返回到一个数组中
* first---获取当前rdd数据集中的第一个元素的值
* 转换操作---map flatMap
*/
val list = List(1,2,3,4,5)
val intRdd = sc.parallelize(list)
val takeR = intRdd.take(1)
print("前三个元素为")
takeR.foreach(println)
val firstR = intRdd.first()
println("第一个元素为"+firstR)
(四)【综合练习】使用Spark RDD分析车辆违章记录
1.根据交通违章数据创建RDD
2.找出扣分最多的交通违章条目
scala
Logger.getLogger("org").setLevel(Level.OFF)
val spark = SparkSession
.builder()
.appName("违章条目统计")
.master("local")
.getOrCreate()
val sc = spark.sparkContext
//基于volation.txt文件创建RDD
val volationRdd = sc.textFile("F:\\spark\\2025\\data\\volation.txt")
//(1)对rdd中的元素做切分操作------map(split)
val splitRdd = volationRdd
.map(x=>x.split("\t"))
//splitRdd.take(6).foreach(x=>println(x.length))
//(2)对所有的元素只保留:违章代码 违章内容 扣分 罚款
.map(x=>(x(0),x(1),x(2).toInt,x(3).toInt))
//(3)按照扣分进行降序排序
.sortBy(x=>x._3,false)
//(4)拿到第一个元素
.first()
//<142004>超过规定时速50%的扣【12】分
println("<"+splitRdd._1+">"+splitRdd._2+"扣【"+splitRdd._3+"】分")
3.查找某车辆的违章记录
scala
/*
任务:需要查找车辆MU0066在本地及临市B的交通违章记录
1.需要将两个数据文件进行合并---union
2.筛选车牌号=MU0066的记录---filter
*/
//1.设置日志级别
Logger.getLogger("org").setLevel(Level.OFF)
//2.创建SparkSession对象
val spark = SparkSession
.builder()
.appName("rdd5")
.master("local")
.getOrCreate()
//3.SparkContext对象的获取
val sc = spark.sparkContext
//4.读取违章数据文件,生成rdd---textFile
val recordA = sc.textFile("D:\\spark\\data\\record.txt")
val recordB = sc.textFile("D:\\spark\\data\\recordCityB.txt")
/*
5.删除两个数据集中的第一个元素
(1)获取两个数据集中的第一个元素---first
(2)筛选出不是第一个元素的数据---filter
*/
val firstA = recordA.first()
val firstB = recordB.first()
val filterA = recordA.filter(x=> !x.equals(firstA))
val filterB = recordB.filter(x=> !x.equals(firstB))
// 6.第5步去表头的两个rdd合并---union
val unionRdd = filterA.union(filterB)
// 7.对合并后的rdd的所有元素做切分操作---map(split)
val splitRdd = unionRdd.map(x=>x.split("\t"))
// 8.筛选车牌号=MU0066的记录---filter 车牌号是数组的第三个元素
val filterRdd = splitRdd.filter(x=>x(2).equals("MU0066"))
// 9.对7筛选后的数据按照时间进行排序---sortBy 时间是数组的第二个元素
val sortRdd = filterRdd.sortBy(x=>x(1))
//sortRdd.foreach(println)
println("-------车辆MU0066的违章信息如下---------")
sortRdd.foreach(x=>{
val date = x(1)
val monitorId = x(0)
val weizhangId = x(5)
println("日期【"+date+"】,监控设备号:【"+monitorId+"】,违章代码:【"+weizhangId+"】")
})
4.找出违章3次以上车辆
scala
/**
* 查找违章3次以上的车辆
* (1)统计出每个车辆的违章次数
* A.按照车辆进行分组------groupByKey
* B.统计每个组中记录的条数
* (2)筛选出次数大于3的违章记录---filter
*/
//1.设置日志级别
Logger.getLogger("org").setLevel(Level.OFF)
//2.创建SparkSession对象
val spark = SparkSession
.builder()
.appName("rdd5")
.master("local")
.getOrCreate()
//3.SparkContext对象的获取
val sc = spark.sparkContext
/*
1.读取数据文件,创建RDD (record.txt)---textFile
2.去掉表头---first filter
3.对所有元素做切分操作---map(split)
4.统计出每个车辆的违章次数
(1)将元素转换为对应的二元组 :(车牌号,1) map
(2)按照key做求和计算操作---reduceByKey
5.找出违章次数大于3的车辆---filter
*/
// 1.读取数据文件,创建RDD (record.txt)---textFile
val recoreRdd = sc.textFile("D:\\spark\\data\\record.txt")
// 2.去掉表头---first filter
val firstR = recoreRdd.first()
val filterRecord = recoreRdd.filter(x=> !x.equals(firstR))
//3.对所有元素做切分操作---map(split)
val splitRecord = filterRecord.map(x=>x.split("\t"))
//(1)将元素转换为对应的二元组 :(车牌号,1) map
val mergeRecord = splitRecord.map(x=>(x(2),1))
// (2)按照key做求和计算操作---reduceByKey
val reduceRecord = mergeRecord.reduceByKey((a,b)=>a+b)
//5.找出违章次数大于3的车辆---filter
val result = reduceRecord.filter(x=>x._2>3)
//6.输出打印结果 车牌号:【CZ8462】,违章次数:【6】次
result.foreach(x=>println("车牌号:【"+x._1+"】,违章次数:【"+x._2+"】次"))
5.打印累积扣12分以上车辆信息
6.将处理结果写入文件
scala
//1.设置日志级别
Logger.getLogger("org").setLevel(Level.OFF)
//2.创建SparkSession对象
val spark = SparkSession
.builder()
.appName("rdd5")
.master("local")
.getOrCreate()
//3.SparkContext对象的获取
val sc = spark.sparkContext
/*
打印累积扣12分以上车辆信息
record---车牌号 违章代码
volation---违章代码 扣分
owner---车牌号 车辆所有人 联系方式
1.读取数据集,创建3个RDD---textFile
2.对3个RDD去表头---first filter
3.对每个rdd中的所有元素,进行切分操作---map split
4.将元素转换为包含有价值信息的字段的元组---map 数组中元素的访问
*/
//1.读取数据集,创建3个RDD---textFile
val record = sc.textFile("D:\\spark\\data\\record.txt")
val volation = sc.textFile("D:\\spark\\data\\volation.txt")
val owner = sc.textFile("D:\\spark\\data\\owner.txt")
//2.对3个RDD去表头---first filter
val recordFirst = record.first()
val volationFirst = volation.first()
val ownerFirst = owner.first()
val recordReal = record.filter(x=> !x.equals(recordFirst))
val volationReal = volation.filter(x=> !x.equals(volationFirst))
val ownerReal = owner.filter(x=> !x.equals(ownerFirst))
//3.对每个rdd中的所有元素,进行切分操作---map split
val recordSplit = recordReal.map(x=>x.split("\t"))
val volationSplit = volationReal.map(x=>x.split("\t"))
val ownerSplit = ownerReal.map(x=>x.split("\t"))
//4.将元素转换为包含有价值信息的字段的元组---map 数组中元素的访问
// record---车牌号 违章代码
val kvrecord = recordSplit.map(x=>(x(5),x(2)))
// volation---违章代码 扣分
val kvvolation = volationSplit.map(x=>(x(0),x(2)))
// owner---车牌号 车辆所有人 联系方式
val kvowner = ownerSplit.map(x=>(x(0),(x(1),x(4))))
/*
累积扣12分以上车辆信息
5.统计出每个车辆的扣分情况
(1)将kvrecord和kvvolation做连接操作---leftOuterJoin
(2)转换为(车牌号,扣分)键值对---map
6.计算出每个车辆的累计扣分---reduceByKey
*/
//(1)将kvrecord和kvvolation做连接操作
//5.统计出每个车辆的扣分情况
val recordJoinV = kvrecord.leftOuterJoin(kvvolation)
//(违章代码,(车牌号,Some(扣分)))
//recordJoinV.foreach(println)
//(车牌号,扣分)
val records = recordJoinV.map(x=>(x._2._1,x._2._2.get.toInt))
//records.foreach(println)
//6.计算出每个车辆的累计扣分---reduceByKey (车牌号,扣分) kvowner:(车牌号,(车辆所有人 联系方式))
val penalize = records.reduceByKey((a,b)=>a+b)
val infor = penalize.leftOuterJoin(kvowner)
//infor.foreach(println)
//(M98772,(2,Some((刘松鑫,1331846239))))
// (车牌号,扣分数,车辆所有人,联系方式)
val result = infor.map(x=>(x._1,x._2._1,x._2._2.get._1,x._2._2.get._2))
//result.foreach(println)
//QR1253,3,杨俊辉,1356752354
//1356752354:【杨俊辉】,您名下车辆【QR1253】违章扣分累计达【3】分,请及时处理!
result.foreach(x=>{
val phone = x._4
val name = x._3
val id = x._1
val koufen = x._2
println(phone+":【"+name+"】,您名下车辆【"+id+"】违章扣分累计达【"+koufen+"】分,请及时处理!")
})