pyspark

spark

用于大规模数据处理的统一(适用面广)分析引擎(数据处理)。

RDD:弹性分布式数据集。

rdd是一种分布式内存抽象,能够在大规模集群中做内存运算,并且有一定的容错方式。

spark 特点:

对任意类型的数据进行自定义计算。
spark 可以计算: 结构化,半结构化,非结构化等各种类型的数据结构,同时也支持使用python ,java scala,R以及sql语言去开发应用程序计算数据。

spark和 hadoop 区别

spark做海量数据的计算,可以进行离线批处理,以及实时流处理。

spark四个特性

1)速度快,
spark支持内存计算,并且通过DAG(有向无环图)执行引擎支持无环数据流,其在内存中的运算速度要比mapreduce快100倍,在硬盘中要快10倍。
spark 与mapreduce处理数据对比:
其一,spark处理数据时,可以将中间处理结果数据存储到内存中。
其二,spark提供了非常丰富的算子(Api),可以做到复杂任务在一个spark程序中完成。
2)易于使用
3)通用性强
spark提供了包括dpark  sql,  dpark  streaming,  mlib  以及graphx在内的多个工具库。
4)运行方式
包括hadoop和  mesos上,也支持standalone的独立运行方式,也可以运行在云kubernetes上。

spark 运行模式

1)本地模式(单机)  local
以一个独立的进程,通过其内部的多个线程来模拟整个spark的运行环境。
2)standalone模式(集群)
各个角色以独立进程的形式存在,组成spark集群环境。
3)kubernetes模式(容器集群),简称k8s集群
各个角色运行在kubernetes的容器内部,组成spark集群环境。
4)云服务模式(运行在云平台上)
后三个用在生产环境。

spark的角色

资源层面:
master角色:集群资源管理
worker角色:单机资源管理

任务运行层面:
driver:单个任务的管理
executor角色:单个任务的计算(woker  干活的)

standalone提交spark应用

命令:bin/spark-submit  --master  spark://server7077;

4040, 8080, 18080 端口分别是什么?

4040:  是单个程序运行的时候绑定的端口,可供查看本任务运行情况
8080:是standalone下,master角色的端口。
18080:默认是历史服务器的端口。

job, state, task 的关系?

一个spark程序会被分成多个子任务(job)运行,每一个job会分成多个state(阶段)来运行,每一个state内会分出来多个task(线程)来执行具体任务。

spark on yarn

是两种运行模式,一种是cluster模式,一种是client模式,两者的区别在于driver运行的位置。

两种部署模式的区别?

clustet模式: driver运行在yarn容器内部,和applicationmaster在同一个容器内。(生产环境中使用该模式)
1)driver程序在yarn集群中,和集群的通信成本低
2)driver输出结果不能再客户端显示
3)该模式下driver运行applicationmaster这个节点上,由yarn管理,如果出现问题,yarn会重启applicationmaster(driver)

client模式: driver运行在客户端进程中,比如driver运行在spark-submit程序的进程中。(学习测试时使用,生产不推荐,要用也可以,性能,稳定性略低)
1)driver运行在client端,和集群的通信成本高
2)driver输出结果能再客户端显示

为什么spark用yarn

提高资源利用率,在已有的yarn场景下,让spark收到yarn的调度可以更好的管控资源提高利用率并方便管理。

pyspark

是应用程序,客户端程序,提供交互式的Python客户端用于写spark api.
pandas用于:小规模数据集的处理
spark用于:大规模数据集的处理。

spark和pyspark的区别

pyspark 用法代码

获取sparkcontext对象,作为执行环境入口

pyspark 执行原理:

driver端由python翻译成jvm,driver端的jvm和executor端的jvm互相通信,通过中转站底层由Pythonexecutor在运行。

rdd

为什么需要rdd:

在分布式框架中,需要有一个统一的数据抽象对象,来实现分布式计算所需功能。

rdd五大特性

  1. 有分区
    rdd分区是rdd数据存储的最小单位, 一份rdd的数据本质上是分割成了多个分区
    例子:

    sc.parallelize([1,2,3,4,5,6,7,8,9],3).glom().collect()
    结果是 [[1,2,3],[4,5,6],[7,8,9]]

2). rdd的方法会作用到其所有的分区上
例子

sc.parallelize([1,2,3,4,5,6,7,8,9],3).map(lambda  x: x*10).glom().collect()
结果是  [[10,20,30],[40,50,60],[70,80,90]]

3). rdd有依赖关系
4). k-v 型的rdd可以有分区器
默认的分区器:hash分区规则,可以手动设置一个分区器(rdd.partitionby的方法来设置 )。
这个特性是 可能得,不是所有的rdd都是k-v型。
k-v rdd:rdd 存储的是二元元祖。
二元元祖: 只有两个元素的元祖,比如("hadoop",6)
5) rdd分区规划,尽量靠近数据所在的服务器。
因为这样可以走本地读取,避免网络读取。

wordcount结合rdd特性进行执行分析

rdd的创建

2种方式:
1.通过并行化集合创建(本地对象 转 分布式rdd);

使用方法:rdd=sparkcontext.parallerize(集合对象,分区数)

2.读取外部数据源(读取文件)

使用方法: sparkcontext.textfile( 文件路径,最小分区数量)

例子

from pyspark import SparkConf, SparkContext

if __name__ == '__main__:
#构建sparkcontext对象
conf = SparkConf().setAppName("test").setMaster(" ocal[*]")
sc = SparkContext(conf=conf)
#通过textFileAPI 读取数据

#读取本地文件数据
file_rdd1 = sc.textFile("../data/input/words.txt")
print("默认读取分区数:",file_rdd1.getNumPartitions())
print("file_rdd1 内容;",file_rdd1.collect())
#最小分区数参数的测试
file_rdd2 = sc.textFile("../data/input/words.txt", 3)
#最小分区数是参考值,spark有自己的判断,你给的太大spark不会理会
file_rdd3 = sc.textFile("../data/input/words.txt",100)
print("file_rdd2 分区数:",file_rdd2.getNumPartitions()
print("file_rdd3 分区数:", file_rdd3.getNumPartitions())
#读取HDFS文件数据测试
hdfs_rdd = sc.textFile("hdfs://node1:8020/input/words.txt")print("hdfs_rdd 内容:",hdfs_rdd.collect())

wholetextfiles

读取小文件比较多时,使用wholetextfiles(文件路径,分区数 )
因为文件的数据很小分区很多,导致shuffle的机率更高,所以尽量少分区读取数据。

from pyspark import SparkConf, SparkContext

if __name__ =='__main.__':
conf = SparkConf() .setAppName("test").setMaster("local[*]")
sc = SparkContext(conf=conf)

#读取小文件文件夹
rdd= sc.wholeTextFiles("../data/input/tiny-files")
print(rdd.map(lambda x:x[1]).collect())

rdd算子

算子:分布式集合对象上的api.
方法/函数: 本地对象的api

2). rdd的算子分类

transformation: 转换算子
定义: rdd的算子,返回值仍是一个rdd.
特性: 这类算子是lazy懒加载的,如果没有action 算子,transformation是不工作的。

action:动作(行动)算子。
定义:返回值不是rdd.

transformation算子相当于在构建执行计划,action是一个指令让这个执行计划开始工作。
如果没有transformation,action算子之间的迭代关系,就是一个没有通电的流水线,只有action到来,这个数据处理的流水线才开始工作。

常用的transformation算子

1) map算子

功能:map算子是将rdd的数据一条条处理( 处理的逻辑基于map算子中接收的处理函数 ),返回新的rdd。

语法: rdd.map( func)

例子:

2) flatmap算子

功能:对rdd执行map操作,然后进行解除嵌套操作。(就是将多维数组转为一维数组)

3) reduceBykey算子

功能:针对kv型的rdd,自动按照key分组,然后根据你提供的聚合逻辑,完成组内数据(value)的聚合操作。
语法: rdd.reduceByKey(func)
按照key分组,值累加

4) mapValues 算子

功能:针对二元元祖rdd,对其内部的二元元祖的value执行map操作。
语法:rdd.mapValues(func)
代码:

sc.parallelize([("a",1), ("a",5), ("a",8), ("b",3)] )
#rdd.map(lambda x: x[0] ,x[1] *10 ).collect()
rdd.mapValues(lambda value: value *10 ).collect()
结果是 [("a",10), ("a",50), ("a",80), ("b",30)]

5) groupBy算子

功能:将rdd的数据进行分组
语法:rdd.groupBy(func)

6)filter算子

功能: 过滤想要的数据进行保留。
返回是true的数据被保留,false将被丢弃
例子:

rdd=sc.parallelize([1,2,3,4,5,6])
result=rdd.filter(  lambda  x: x %2==1)
print(result.collect())
结果是[1,3,5]

7) distinct算子

功能:对rdd数据进行去重,返回新的rdd。
语法:rdd.distincr(参数1) #参数1,去重分区数量,一般不用传
例子:

rdd = sc.parallelize(  [1,1,1,2,3])
print(rdd.distinct().collect())
结果是  [1,2,3]

8)union算子

功能:两个rdd合并成一个rdd返回。
注:union 不会去重; rdd的类型不同也可以合并。
例子

rdd1 = sc.parallelize(  [1,1,2])
rdd2 = sc.parallelize(  ["a","b","a"])
rdd3 = rdd1.union(rdd2)
print(rdd3.collect())
结果是[1,1,2,"a","b","a"]

9) join算子

功能:对两个rdd执行join操作(可实现sql的内外连接 )
语法: rdd.join(other_rdd) #内连接
rdd.leftouterjoin(other_rdd) #左外
rdd.rightouterjoin(other_rdd) #右外

10) intersetion算子

功能:求两个rdd的交集,返回一个新的rdd。
例子

rdd1 = sc.parallelize(  [("a",1),("b",1])
rdd2=sc.parallelize( [("a",1),("c",1]  )
rdd3 = rdd1.intersetion(rdd2).collect()
结果是["a",1]

11) glom算子

功能:将rdd的数据,加上嵌套,这个嵌套按照分区进行。
方法: rdd.glom()
例子:

rdd = sc.parallelize([1,2,3,4,5], 2)
rdd.glom().collect()
结果是[[1,2,3],[4,5]]

12) groupbykey算子

功能:针对kv型rdd,自动按照key分组。
例子

rdd1 = sc.parallelize(  [("a",1),("b",1],("a",1),("b",1),  ("b",1)  )
rdd2  = rdd1.groupbykey()
print( rdd2.map( lambda x : (x[0],list(x[1]) ) ).collect()
结果:  [("a",[1,1]),("b",[1,1,1])]

13) sortBy算子

功能: 对rdd数据进行排序
语法:

rdd.sortby(func,ascending =true,numpartition=1)
ascending  true升序,false降序
numpartition   用多少分区排序

例子

rdd = sc.parallelize(  [("a",1),("b",9],("a",3),("b",2),  ("b",6)],3  )
print(rdd.sortby(lambfa x:x[1],ascending=true,numpartition=3  )))#按值排序, x[0]是按key排序
结果  [("a",1),("b",2],("a",3),("b",6),  ("b",9)]

14) sortByKey 算子

功能: 针对kv型rdd,按照key排序。
语法: sortbykey( ascending=true,numpartition=3, keyfunc=xxx)
keyfunc: 在排序前对key进行处理。
例子

rdd = sc.parallelize(  [("a",1),("f",1],("C",1),("B",1),  ("h",1)],3  )
print(rdd.sortbykey(keyfunc=lambda key:str(key).lower(),ascending=true,numpartition=1  ))).collect()
结果是[("a",1),("B",1],("C",1),("f",1),  ("h",1)]

将案例提交到yarn执行

常用的action算子, (其结果不是rdd类型了)

1) countBykey算子

功能:统计key出现的次数,(一般适用于kv型的rdd)
例子

rdd = sc.textfile( ./t.txt)
rdd1 = rdd.flatmap(lambda  x: x.split(" ")).map(lambda x :(x,1))
result = rdd1.countBykey()
print(result)
结果 ({"hello": 3, "foo" : 2})

2) collect()

功能:将rdd各个分区内的数据,统一收集到driver中,形成一个list对象。

3)reduce算子

功能: 对rdd数据集按照你传人的逻辑进行聚合。
例子

rdd = sc.parallelize([1,2,3,4])
print(rdd.reduce( lambda a,b : a+ b))
结果  10

4)fold算子

功能:和reduce一样进行聚合,聚合是带有初始值的,这个初始值聚合会作用在: 分区内聚合,分区间聚合。
比如[[1,2,3],[4,5,6],[7,8,9]]
数据分布在三个区,
分区1,123聚合时带上初始值10得到16,
分区2,456聚合时带上初始值10得到25,
分区1,789聚合时带上初始值10得到34
三个分区的结果做聚合带上初始值10,所以结果是10+16+25+34 =85。
例子

rdd = sc.parallelize([1,2,3,4,5,6,7,8,9])
print(rdd.fold(10, lambda a,b:a + b)

5) first()算子

功能:取出rdd的第一个元素。
例子

sc.parallelize([1,2,3,4,5,6,7,8,9]).first()
结果是 1

6) take算子

功能: 取rdd的前N个元素,返回列表
例子

sc.parallelize([1,2,3,4,5,6,7,8,9]).take(5)
结果是[1,2,3,4,5]

7)top算子

功能: 对rdd数据进行降序排序,取前N个。
例子

sc.parallelize([1,2,9,6,8,5]).top(3)
结果是[9,8,6]

8) count算子

功能: 计算rdd有多少条数据
例子

sc.parallelize([1,2,9,6,8,5]).count()
结果是6

9) takeSample算子

功能: 随机抽样rdd数据
用法:

takeSample( true  or  false,采样数,随机数种子  )
true  or  false: 是否允许取同一个位置的数据(不是值相同)
采样数: 抽样要几个
随机数种子:随意给一个数,如果传同一个数字,那么取出的结果是一样的,(一般不传,spark会自动给随机数种子)

例子

rdd = sc.parallelize([1,2,9,6,8,5])
print(rdd.takeSample(true,8))
结果是[1,2,5,6,8,2,6,8]  随机取出了8位数

10) takeOrdered 算子

功能:对rdd进行排序取前N个
用法:

rdd.takeOrdered(参数1,参数2)
参数1,要几个数据
参数2,对排序的数据进行更改(不会改原数据本身)

例子

rdd = sc.parallelize([1,2,9,6,8,5])
print(rdd.takeOrderd(3))  # [1,2,5]
print(rdd.takeOrderd(3,lambda x :-x))  #[9,8,6]

11) foreach 算子

功能:对rdd的每一个元素,执行你提供的逻辑操作,没有返回值(直接在executor里返回值,driver就不负责返回值了,减少driver的压力,效率可能会高一点)
用法:rdd.foreach(func)
例子:

rdd = sc.parallelize([1,2,9,6,8,5])
rdd.foreach(lambda x : print(x *10))
#可以把print写到lambda里。

12)saveAsTextFile算子

功能: 将rdd的数据写入到文本文件中,支持本地写出,HDFS等文件系统。
例子

rdd = sc.parallelize([1,2,9,6,8,5],3)
rdd.saveAsTextFile(hdfs://node1:8020/output  )

注意: foreach和saveastextfile 算子是分区(executor)直接执行的,跳过driver,由分区所在的executor直接执行; 反之, 其余的action算子都会将结果发送到driver。所以这两个算子性能比较好,不会爆内存。

分区操作算子

1) mapPartitions算子

mappartition一次被传递是一整个分区的数据,作为一个迭代器(一次性list)对象传人过来。

例子

rdd = sc.parallelize([1,2,9,6,8,5],3)
def  foo(iter):
     result=list()
     for  i  in  iter:
            result.append(i  *  10)
     return   result
print(rdd.mappartitions( foo ).collect())
结果是[10,20,90,60,80,50]

2) foreachpartition算子

功能:和普通的foreach一致,一次处理的是一整个分区数据。 没有返回值
例子

rdd = sc.parallelize([1,2,9,6,8,5],3)
def  foo(iter):
     result=list()
     for  i  in  iter:
            result.append(i  *  10)
     print(result)
rdd.mappartitions( foo )
#结果是
[10,20]
[90,60]
[80,50]

3) partitionBy 算子

功能: 对rdd进行自定义分区操作。
例子

rdd = sc.parallelize([ ("hadoop",1),  ("hadoop",1),   ("spark",1) , ("spark",1),  ("filnk",1) ,("hello",1) ])
#使用partitionby自定义分区
def  foo(k):
      if  "hadoop" == k  or  "hello"  ==  k:  return   0
      if  "spark" == k :  return   1
      return  2

rdd.partitionby(3,foo).glom().collect())
#结果,[[  ("hadoop",1),  ("hadoop",1),("hello",1) ],   [  ("spark",1),  ("spark"",1)],[("filnk",1) ]   ]

4) repartition算子

功能:对rdd的分区执行重新分区(仅数量)
用法: rdd.repartition(N)
传入N,决定新的分区数

注:如果改分区: 会影响并行计算(内存迭代的并行管道数量 ); 分区如果增加,极大可能导致 shuffle。

例子

rdd = sc.parallelize([1,2,9,6,8,5],3)
#repartition修改分区
print(rdd.repartition(1  ).getNumPartitions())
print(rdd.repartition(5  ).getNumPartitions())
#coalesce修改分区,  必须有shuffle=True,安全,他是安全阀,避免把5按成50,影响性能
print(rdd.coalesce(1  ).getNumPartitions())
print(rdd.coalesce(5,shuffle=True  ).getNumPartitions())
结果是  1    5    1    5

面试题: grouobykey 和 reducebykey 区别?

功能上: grouobykey 仅仅只有分组功能
reducebykey 除了有bykey的分组功能外还有reduce的聚合功能
性能上:reducebykey 的性能远大于grouobykey + 聚合逻辑的。
groupbykey 是先分组后聚合
reducebykey 是先预聚合,在分组,最后再聚和。这样的话网络io的开销是最小的。

问题2: transformation和action的区别?

转换算子的返回值100%是rdd,而action的返回值100%不是rdd,转换算子是懒加载,只有遇到action才会执行,action就是转换算子处理链条的开关。

rdd 的数据是过程过程

rdd之间进行相互迭代计算(transformation的转换,当执行开启时,新rdd生成,旧的rdd消失。
特性: 最大化的利用资源,旧的rdd没有了就从内存中清理,给后续的计算腾出空间

rdd缓存

rdd缓存:可以通过调用api,将指定的rdd数据保留在内存或是硬盘上缓存的api。
用法:

rdd不需要再走之前的链条,重新去计算了,而是直接保存在内存中,想用就去用。

缓存的目的:避免迭代的链条重新计算。
缓存是不安全的,缓存如果丢失?
就要重新计算重新缓存了,缓存必须保留 被缓存rdd的前置血缘关系。
例子

rdd checkPoint

也是将rdd的数据保存起来,但是仅支持硬盘存储。并且被设计认为是安全的(缓存不会丢失),不保留血缘关系(只能重新计算)。

checkPoint是如何保存数据的?

是被保存到hdfs上的,如图,checkpoint存储rdd数据,是集中收集各个分区数据进行存储,而缓存是分散存储。
看截图

缓存 和 checkpoint 对比:

1) checkpoint不管分区数量多少,风险是一样的 ; 缓存分区越多,风险越高。
2)  checkpoint 支持写入HDFS ,缓存不行,HDFS是高可靠存储,checkpoint 被认为是安全的。
3) checkpoint 不支持内存,缓存可以,缓存如果写内存,性能比checkpoint要好一些。
4)checkpoint 被设计认为是安全的,所以不保留血缘关系,而缓存因为设计上认为不安全,所以保留。

用法:

sc.setCheckpointDir("hdfs://node1:8020/output/ckp")
#用的时候,直接调用checkpoint算子即可.
rdd.checkpoint()

例子

spark广播变量



用法:

#1.将本地list 标记成广播变量即可
broadcast = sc.broadcast(stu_info_list)
#2.使用广播变量,从broadcast对象中取出本地List对象即可
value = broadcast(value)
#也就是 先放进去broadcast内部,然后从broadcast内部在取出来用,中间传输的是broadcast这个对象了
#只要中间传输的是broadcast对象,spark就会留意,只会给每个Executor发一份了,而不是傻傻的哪个分区要都给

如果将本地list对象标记为广播变量对象,那么spark只会给每个executor来一份数据,而不是像原本数据那样,每一个分区的处理线程都来一份,节省内存。

问题1:为什么不把本地集合写成分布式rdd形式?
数据量不大的时候,使用本地集合是性能提升的一个点,避免了大量的shuffle。

本地集合对象 和 分布式集合对象(rdd)进行关联时,需要将本地集合对象封装成广播变量,可以节省内存:1.网络io的次数,2. executor的内存占用。
例子:

# codinq;utf8

if-_name_- ==__main_':
  conf= SparkConf().setAppName("test").setMaster("local[*]")
  sc = SparkContext(conf=conf)
  stu_info_list =[(1,'张大仙',11),
                  (2,'王晓晓’,13),
                  (3, '张甜甜”,11)
                  (4,'王大力',11)]
  #1.将本地Python List对象标记为广播变量
  broadcast = sc.broadcast(stu_info_list)
  score_info_rdd = sc.parallelize([
      (1,'语文',99),
      (2,'数学',99),
      (3,'英语’,99),
      (4,'编程',99),
      (1,'语文',99),
      (2,'编程,99)
      (3,'语文',99)
      (4,'英语',99)04
      (1,'语文',99)
      (3,'英语',99)
      (2,'编程',99)
    ])

  def map_func(data):
    id = data[0]
    name = #u
    #匹配本地List和分布式rdd中的学生ID 匹配成功后 即可获得当前学生的姓名#2.在使用到本地集会利象时方,从广播变量中取出来用即司
    for stu_info in broadcast.value:
    stu_id = stu_info[0]
    if id == stuid:
      name = stu_info[1]
    return (name,data[1], data[2])
  print(score_info_rdd.map( func).collect())

累加器

想要对map算子计算中的数据,进行技术累加,得到全部数据计算完后的累加结果。
代码:

sc.parallelize([1,2,9,6,8,5],2)
#spark提供的累加器变量,参数是初始值
acmlt = sc.accumulater(0)
def  map_func(data  )
       global   acmlt
       acmlt += 1
       print(acmlt)     # 1  2  3  1  2  3
rdd.map(map_func).collect()
print( acmlt )   #  6

DAG

  1. DAG: 有向无环图,有方向没有形成闭环的一个执行流程图。
    2)作用:
    是协助DAG调度器构建task分配用以做任务管理。

job 和 action

1个action会产生一个DAG,如果代码中有3个action,就产生3个DAG,1个action产生的一个DAG会在程序运行中产生一个job。
所以1个action = 1个job = 1个 DAG.

1个代码运行起来,在spark中称之为 application。

层级关系:
1个application中可以有多个job,每一个job内含一个DAG, 同时每一个job都是由一个action产生。

2.DAG和 分区

带有分区的DAG

3.DAG的宽窄依赖和阶段划分。

窄依赖: 父rdd的一个分区,全部将数据发给子rdd的一个分区。
宽依赖: 父rdd的一个分区,将数据发给子rdd的多个分区。
宽依赖还有一个别名:shuffle。

4。阶段划分

对于spark来说,会根据DAG,按照宽依赖,划分不同的DAG阶段。
**划分依据: ** 从后向前,遇到宽依赖就划分出一个阶段,称之为stage。
在stage内部一定都是: 窄依赖。

内存迭代计算

面试题1. spark是怎么做内存计算的? DAG的作用? stage阶段划分的作用?

面试题2. spark为什么比mapreduce快

spark并行度

先有并行度,才有分区规划。
spark的并行: 在同一时间内,有多少个task在同时运行。

并行度:并行能力的设置
比如设置并行度6,其实就是要6个task并行在跑。
在有了6个task并行的前提下,rdd的分区就被规划成6个分区了。

2) 如何设置并行度

优先级从高到低:
代码中
conf = SparkConf()
conf.set( "spark.default.parallelize" ,"100"  )
客户端提交参数中
b8n/spark-submint  --conf  "spark.default.parallelize=100"
配置文件中
conf/spark-defaults.conf 中设置
spark.default.parallelize 100

默认(1,但是不会全部以1来跑,多数时候基于读取文件的分片数量  来作为默认并行度) 

全局并行度配置的参数:  spark.default.parallelize

注意:全局并行度是推荐设置,不要针对rdd该分区,可能会影响内存迭代管道的构建,或者会产生额外的shuffle。

spark任务调度

driver内的两个组件

1)DAG调度器
工作内容:将逻辑的DAG图进行处理,最终得到逻辑上的task划分。

2)task调度器
工作内容: 基于DAG scheduler 的产出,来规划这些逻辑的task,应该在哪些物理的executor上运行,以及监控管理他们的运行。

spark运行的层级关系

.sparkSQL

**分布式SQL计算引擎: ** sparksql ,hive, impala, presto。

sparksql是spark的一个模块,用于处理海量结构化数据。

sparksql支持SQL语言,性能强,可以自动优化,api简单,兼容hive等等。
sparksql处理业务数据: 离线开发,数仓搭建,科学计算,数据分析。
1)特点:

融合性:sql可以无缝集成在代码中,随时用SQL处理数据。
统一数据访问:  一套标注api可读写不同数据源。
hive兼容:  可以使用sparksql直接计算并生成hive表。
标准化链接: 支持标准化JDBC/ODBC链接,方便和各种数据库进行数据交互。

2). sparksql 和 hive的异同

3) .sparksql的数据抽象

有三种: SchemaRDD(废弃),dataFrame(python,java,scala ,R ), DataSet(scala ,java)
dataframe 和 dataset对比,基本相同,不同的是dataset 仅支持泛型特性,可以让Java,scala语言更好的利用到。

28.sparksession对象

在rdd阶段,程序的执行入口对象是:sparkcontext,在spark2.0之后,推出了sparksession 对象作为spark编码的统一入口对象。所以 后续的代码执行环境入口对象,统一变更为sparksession对象。
用法:

代码例子

datafram是按照二维表格的形式存储数据

rdd是存储对象本身(字符串,list,dict等形式).

datafram更适合SQL进行处理。

dataframe 组成结构:
structType对象描述整个dataframe 的表结构
Structfield 对象描述的是一个列的信息
row对象记录一行数据
Column 对象记录一列数据并包含列的信息

dataframe代码构建

将rdd转成dataframe
1)spark.createDataFrame( rdd,schema=["name","age"] )
代码如下:

2) 通过structype对象来定义dataframe的表结构
schema= StructType().add(列名,列类型,是否允许为空)
例子

3) 使用rdd的toDF转换rdd。
例子

4)将pandas的dataframe转换成分布式的sparksql dataframe对象
例子

通过spark SQL的统一api进行数据读取构建dataframe。

1)读取TXT文件

2)dataframe读取json数据

3)读取csv数据源

4)读取parquet数据源
parquet:是spark中常用的一种列式存储文件格式。

parquet跟普通文件对比:
parquet内置schema(列名/列类型,是否为空);
存储是以列作为存储格式;
存储是序列化存储在文件中(有压缩属性体积小)
示例:

df = spark.read.format( "parquet" ).load("./xx.parquet")
df.printschema()
df.show()

dataframe的两种分格

DSL风格:比如,df.where().limit()

sql风格:比如,spark.sql(select * from xxx)
使用sql风格的语法,需要将dataframe注册成表。

df.createTempView("score" )#注册一个临时表
df.createOrReplaceTempView("score" )#注册一个临时表,如果存在进行替换。
df.createGlobalTempView("score" )#注册一个全局表
全局表:跨sparksessi9n对象使用,在一个程序内的多个sparksession 中均可调用,查询前带上前缀,global_temp.
临时表:只在当前sparksession中使用。

代码

DSL - printSchema
功能: 打印输出df的schema信息
语法: df.printShema()

pyspark.sql.functions包

这个包里面提供了一系列的计算函数供sparksql使用。
使用:

导包  from  pyspark.sql  import  functions  as F
F.split(被切分的列,切分字符串)
F.explode(被转换的列  )
这些功能函数,返回值多数都是column对象。

sparksql 数据清洗api

去重方法 dropDuplicates
用法:

df.dropDuplicates().show()  #无参数是对数据整体去重
df.dropDuplicates(["name","age"]).show()#针对字段去重
dropna  删除空行
fillna  填充    

例子

dataframe数据写出

统一api语法:

df.write.mode().format().option(k,v).save(path)
mide:append 追加,overwrite  覆盖,ignore  忽略 ,error  重复就报异常(默认的)
option:  设置属性
save  写出的路径

例子

dataframe通过JDBC读写数据库

需要安装MySQL驱动包才行
代码

sparksql定义UDF函数

UDF:一对一的关系,输入一个值输出一个值。

定义方式有2种:

  1. sparksession.udf.register()
    注册的udf可以用于DSL和sql,返回值用于DSL风格,传参内的名字可用于sql风格。
    2).pyspark.sql.functions.udf
    仅用于DSL风格


posted on 2023-01-12 23:17  xm微微一笑  阅读(206)  评论(0编辑  收藏  举报

导航