spark 是基于内存的计算框架。
RDD是分区的,保存在不同的工作节点上,本质上是一个只读的分区记录集合,数据片段,rdd转换的过程中,因此通过生成新的RDD来完成一个数据修改的目的。
DAG(directed Acyclic Graph):有向无环图
spark集群模式:mesos,yarn,stando
pyspark命令及其常用的参数如下:
pyspark --master # 后面不同的参数,表示可以进入不同的交互式环境(单机还是集群)
spark 的运行模式取决于传递给sparkcontext的master url的值,mastr url可以是一下任意一种形式
- local 使用一个Worker线程本地化运行SPARK(完全不并行)
- local[*] 使用逻辑cpu个数数量的线程本地化运行spark
- local[K]:使用K个Worker线程本地化运行SPARK
- spark://HOST:PORT 连结到指定的spark standalone master。默认端口是7077
- yarn-client 以客户端模式连结YARN集群,集群的位置可以在HADOOP_CONF_DIR环境变量中找到 程序员调试时应用
- yarn-cluster 以集群模式连结YARN集群,集群的位置可以在HADOOP_CONF_DIR环境变量中找到
- mesos://HOST:PORT 连结到指定的Mesos集群,默认端口5050
在spark中采用本地模式启动pyspark命令主要包含一下参数
--master
:这个参数表示当前的pyspark要连接到哪个master
--jars
这个参数用于把相关的JAR包添加到CLASSPATH中,如果有多个jar包,可以使用逗号分隔符连接它们。
采用本地模式,在4个cpu核心上运行pyspark
cd /usr/local/spark # spark 安装路径
./bin/pyspark --master local[4]
# ./bin/pyspark --master local[4] --jars code.jar # 可以在CLASSPATH中添加code.jar
exit() 退出交互式环境,返回到linux环境
通过spark-submit运行程序
#开发spark独立应用程序
wordcount.py
from pyspark import SparkConf,SparkContext
conf=SparkConf().setMaster('local').setAppName('My App') # 生成上下文配置
sc=SparkContext(conf=conf) # 生成一个sparkcontext对象
logFile='file:///usr/local/spark/README.md' # 三个///
logData=sc.textFile(logFile,2).cache() #生成一个RDD
numAs=logData.filter(lambda line:'a' in line).count()
numBs=logData.filter(lambda line :'b' in line).count()
print("Lines with a:%s,Lines with b: %s" %(numAs,numBs))
执行程序:
python3 WordCount.py
使用python解释器
#通过spark-submit提交应用程序
spark-submit
--master<master-url>
--deploy-mode <deploy-mode> #部署模式
....# 其他参数
<application-file> #python代码文件
[application-argyments] # 传递给主类的主方法的参数
通过spark-submit提交应用程序
/usr/local/spark/bin/spark-submit /usr/local/spark/mycode/python/WordCount.py
为了避免其他多余信息对结果产生干扰 ,可以修改log4j的日志信息显示级别
log4j.rootCategory=INFO,console ---- > 改为log4j.rootCategory=ERROR,console
在集群中运行应用程JAR包
cd /usr/local/spark/ # 进入spark的安装目录
bin/spark-submit \
--master spark://master:7077 \ # 连结集群模式
/usr/local/spark/examples/src/main/python/pi.py 2>&1| grep "Pi is roughly" # 代码文件
#在集群中运行pyspark
cd /usr/local/spark/ # 进入spark的安装目录
bin/spark-submit --master spark://master:7077 # 连结集群模式,交互是环境
testFile=sc.textFile('hdfs://master:9000/README.md') #生成一个RDD,集群式双//,本地式file:///
textFile.count()
textFile.first() # 取第一行
运行之后查看应用的运行信息
http://master:8080/
向Hadoop yarn集群管理器提交应用
cd /usr/local/spark/
bin/spark-submit \
--master yarn-client \
/usr/local/spark/examples/src/main/python/pi.py
查看运行状态:tracking url
RDD 创建
spark 的sparkcontext通过textFile(文件系统地址) 读取数据生成内存中的RDD
生成的RDD,源文件中每一行表示一个RDD元素
textFile() 支持的数据类型:分布式文件系统HDFS,本都文件系统,Amazo S3等等
***交互式环境下,系统默认已经生成了sparkContext对象 sc
从分布式文件系统HDFS中加载数据(以下三种方法全部是等价的,可以使用其中任意一种方式)
lines=sc.textFile("hdfs://localhost:9000/user/hadoop/word.txt")
lines=sc.textFile("/user/hadoop/word.txt") #生成RDD
lines=sc.textFile("word.txt")
通过并并行集合(数组)创建RDD实例
array=[1,2,3,4,5]
rdd=sc.parallelize(array) # 生成RDD
rdd.foreach(print) # 高阶函数
RDD操作
经常用到的transformation操作
filter(func)
map(func) #生成的RDD中每个元素都是一个list
flatMap(func) flat将上面的结果放在一起,将全部的结果混合在一起 ,将字符串打散为单词
groupBykey() # 分组的结果用迭代器存放
reduceBykey(func)
常见的行动操作Action
count()
collect()
first()
take(n) # 切片
reduce(fuc)
foreach(func)
RDD持久化
1. persist(MEMORY_ONLY) # 内存不足,替换内容,先进先出
2. persist(MEMORY_AND_DISK) # 内存不足,存放磁盘
使用unpersist() 手动地把持久化的RDD从缓存中移除
list=['hadoop','spark','hive']
rdd=sc.parallelize(list)
rdd.cache()
# 会调用persist(MEMORY_ONLY)
,但是,语句执行到这并不会缓存rdd,因为
这是的rdd还没有被计算生成
print(rdd.count())
# 第一次行动操作,触发一次真正从头到尾的计算。这是上面的rdd.cache()
才会被执行
,把这个rdd存放到缓存中
print(' '.join(rdd.collect()))
# 第二次行动操作,不需要触发从头到尾的计算,只需要rdd.cache()
才会被
执行,把这个rdd存放到缓存中
分区:rdd分区被保存到不存的节点上
RDD分区的作用,原则,设置分区的方法
增加并行度,减少通讯开销
spark.default.parallelism()
sc.textFile(path,partitionNum) #partitionNum 用于指定分区个数
可以通过转换操作得到新的rdd时,直接调用repartition方法集合。
len(data.glom().collect()) # 显示data这个rdd的分区数量
rdd=data.reparttition(1) # 对data这个rdd进行重新分区
#自定义分区方法:
from pyspark import SparkConf,SparkContext
def Mypartitioner(key):
print('MyPartitioner is running')
print(''The key is %d "%key)
return key %d
def main():
conf=SparkConf().setMaster('local'.setAppNqme('MyApp'))
sc=SparkContext(conf=conf)
data=sc.parallelize(range(10),5)
data.map(lambdax:(x,1)).partitonBy(10,Mypartitioner).map(lambda x :x[0]).saveAsTextFile
('file:///usr/local/spark/mycode/rdd/partitioner')
if __name__=="__main__":
main()
键值对RDD
键值对RDD的创建
lines=sc.textFile("file://usr/local/spark/mycode/pairrdd/word.txt")
pairRDD=lines.flatMap(lambda line:line.split('')).map(lambda word:(word,1))
pairRDD.foreach(print)
常用键值对RDD转换操作
reduceBykey(func)
groupByKey()
keys 提取key
values 提取value
combineByKey # 不常用
join 表示内连接,将存在共同key的value存放在一起
mapValues(func) # 对键值中的value进行操作
sortByKey(False/True) # 排序操作 sortBy()传入lambda按照key或value进行排序
综合实例:
rdd=sc.parallelize([('spark',2),('hadoop',6),('hadoop',3),('spark',4)])
rdd.mapValues(lambda x:(x,1)).reduceByKey(lambda x,y:(x[0]+y[0],x[1]+y[1])) \
.mapValues(lambda x : x[0]/x[1]).collect() #mapValue是对最外层的value整体做处理
# mapValues(lambda x:(x,1)) 对原键值中的值形成子键值对
# .reduceByKey(lambda x,y:(x[0]+y[0],x[1]+y[1])) # 先按照最外层的key聚合,形成value_list(这里是子键值list)
文件数据读写
1.本地文件系统的数据读写
textFile=sc.textFile('file:///usr/local/spark/mycode/rdd/word.txt')
把RDD写入文本文件、
textFile.saveAsTextFile("file:///usr/local/spark/mycode/rdd/writeback") # 写的时候不指定文件,而是目录(分布式环境)
# 用的时候直接加载,会直接把所有的rdd分区数据加载进来
2.分布式文件系统HDFS的数据读写
textFile=sc.textFile('hdfs://localhost:9000/usr/hadoop/word.txt') # 简写版等价
textFile.saveAsTextFile("writeback") # 写的时候不指定文件,而是目录(分布式环境)
Hbase(分布式数据库):行键,列族,列限定符,时间戳
行:每个HBase表由若干行组成,每个行由行键(row key)来标记
列限定符:列簇里的数据通过列限定符(或列)来定位
单元格:在HBase表中,通过行,列族和列限定确定一个“单元格”(cell),单元格中存储的数据没有数据类型
,总被视为字节数组byte[]
表:HBase采用表来组织数据,表由行和列组成,列划分为若干列族
列族:一个Hbase表被分组成许多“列族”(Column Family)的集合,它是基本的访问控制单元
时间戳:每个单元格都保存着同一份数据的多个版本,这些版本通过时间戳进行索引。
读写HBase数据
# 用sparkContext提供的newAPIHadoopRDD API将表中的内容以RDD的形式加载到spark中
# sparkOperateHBase.py
from pyspark import SparkConf,SparkContext
conf=SparkConf().setMaster('local').setAppName('ReadHBase')
sc=SparkContext(conf=conf)
host='localhost'
table='student'
conf={"hbase.zookeeper.quorum":host,'hbase.mapreduce.imputtable':table} # 指定zookeeper的地址,读取表的名称
# 将hbase表格中的数据转化为需要的字符串格式。
keyConv="org.apache.spark.examples.pythonconverters.ImmutableBytesWritableToStringConverter" #键转换器的转化类
valueConv="org.apache.spark.examples.pythonconverters.HBaseResultToStringConverter" #指定值的转换器的转化类
hbase_rdd=sc.newAPIHadoopRDD("org.apach.hadoop.hbase.mapreduce.TableInputFormat", # 调用api
"org.apache.hadoop.hbase.io.ImmutableBytesWritable",
"org.apache.hadoop.hbase.client.Result",
jeyConverter=keyConv,
valueConverter=valueConv,conf=conf)
count=hbase_rdd.count()
hbase_rdd.cache()
output=hbase_rdd.collect()
for (k,v) in output:
print(k,v)
向Hbase表中写入数据
from pyspark import SparkConf,SparkContext
conf=SparkConf().setMaster('local').setAppName('ReadHBase')
sc=SparkContext(conf=conf)
host='localhost'
table='student'
conf={"hbase.zookeeper.quorum":host,'hbase.mapreduce.imputtable':table} # 指定zookeeper的地址,读取表的名称
keyConv="org.apache.spark.examples.pythonconverters.StringToImmutableBytesWritableConverter" #转换类
valueConv="org.apache.spark.examples.pythonconverters.StringListToPutConverter" #转化类
conf={"hbase.zookeeper.quorum":host,
'hbase.mapred.outputtable':table,
"mapreduce.outputformat.class":"org.apache.hadoop.hbase.mapreduce.TableOutputFormat",
"mapreduce.job.output.key.calss":"org.apache.hadoop.hbase.io.ImmutableBytesWritable",
"mapreduce.job.output.value.calss":"org.apach.hadoop.io.Writable"}
rawData=
['3,info,name,RongCheng','3,info,gender,M','3,info,age,26','4,info,name,Guanhua','4,info,gender,M',"4,info,age,27"]
# info是列族,name是列名称,
sc.parallelize(raw_dat).map(lambda x:(x[0],x.split(,))).saveAsNewAPIHadoopDataset(
conf=conf,keyConverter=keyConv,valueConverter=valueConv)
求top值top.py
from pyspark import SparkConf,SparkContext
conf=SparkConf().setMaster('local').setAppName('ReadHBase')
sc=SparkContext(conf=conf)
lines=sc.textFile("file:///usr/local/spark/mycode/rdd/file")
result1=lines.filter(lambda line:len(line.strip()>0) and (len(line.split(","))==4))
result2=result1.map(lambda x :x.split(",")[2])
result3=result2.map(lambda x : (int(x),""))
result4=result3.reparttion(1)
result5=result4.sortByKey(False)
result6=result5.map(lambda x:x[0])
result7=result6.take(5)
for a in result7:
print(a)
文件排序:(读取不同文件中的所有的整数进行排序)fileSort.py
from pyspark import SparkConf,SparkContext
index=0
def getindex():
global index
index+=1
return index
def main():
conf=SparkConf().setMaster('local').setAppName('FileSort')
sc=SparkContext(conf=conf)
lines=sc.textFile("file:///usr/local/spark/mycode/rdd/filesort/file*.txt") # 所有的文件
index=0
result1=lines.filter(lambda line:len(line.strip())>0)).map(lambda x:(int(x.strip()),'''')) # 通过添加空字符形成键值对
result2=result.repartition(1).sortByKey(True).map(lambda x :x[0]).map(lambda x :(getindex(),x)) # 充分区,使数据全局有序,
result2.foreach(print)
result2.saveAsTextFile("file:///usr/local/spark/mycode/rdd/filesortresult")
二次排序:对于一个文件,先对数据根据一列进行排序,如果相同,按照第二列进行排序 sortbykey 排序
思路:通过Ordered 和serializable接口实现自定义排序的key secondarySortKey.py
from operator import gt
from pyspark import SparkContext,SparkConf
class SecodarySortKey(): # 由原来不可排序的对象生成可以排序的对象
def __inti__(self,k): #k=(5,3)
self.column1=k[0]
self.column2=k[1]
def __gt__(self,other): # 重写原来的比较函数
if other.column1==self.column1:
return gt(self.column2,other.column2)
else:
return gt(self.column1,other.column1)
def main():
conf=SparkConf().setAppName('spart_sort').setMaster('local[1]')
sc=SparkContext(conf=conf)
file="file:///usr/local/spark/mycode/rdd/secondarysort/file4.txt"
rdd1=sc.textFile(file).filter(lambda x:len(x.strip())>0))
rdd2=rdd1.map(lambda x :((int(x.split(" ")[0]),int(x.split(" ")[1])),x)).map(lambda x: (SecondarySortKey(x[0]),x[1])) # ((5,3),"5 3")
rdd3=rdd2.sortByKey(False).map(lambda x:x[1] )
rdd3.foreach(print)
Spark SQL 核心概念dataframe
dataframe的创建:
sparkSession支持从不同的数据源加载数据,并把数据转换成DataFrame,并且支持把DataFrame转换成SQLContext自身中
的表,然后使用SQL语句来操作数据。
1.创建sparkSession
from pyspark import SparkContext,SparkConf
from pyspark.sql import SparkSession
spark=SparkSession.builder.config(conf=SparkConf()).getOrCreate() #创建SparkSeeion对像
2创建DataFrame
df=spark.read.text("people.txt") 或者 spark.read.format("text").load("people.txt") # 读取文本文件people.txt 创建DataFrame
df=park.read.json("people.json") 或者 spark.read.format("json").load("people.json") # 读取people.json 创建DataFrame
df=spark.read.parquet("people.parquet") 或者 spark.read.format("parquet").load("people.parquet") #读取people.parquet文件创建DataFrame
3.dataframe 保存 spark.write操作保存dataframe
df.write.txt("people.txt") # 并不是文件名称,而是目录名称
df.write.json()
df.write.parquet()
- 常用的操作:
df.printSchema() # 模式信息
df.select(df["name"],df["age"]+1).show() # 选择列并显示
df.filter(df["age">20]).show
df.groupBy("age").count().show()
df.sort(df["age"].desc().df["name"].asc()).show()
利用反射机制推断RDD模式,将RDD转化为dataframe对像
数据
Andy 29
from pyspark.sql import Row # Row对像,用封装一行行row对像数据
people=spark.sparkContext.textFile("file:///usr/local/spark/examples/src/main/people.txt").\
map(lambda line :line.split(',')).map(lambda p :Row(name=p[0],age=int(p[1])))
schemaPeople=spark.createDataFrame(people),将rdd转换为df
schemaPeople.createOrReplaceTempView("people") “people”为临时表的名称 # 必须注册为临时表才能供下面的查询使用,这里创建临时表
personsDF=spark.sql("select name,age from people where age >20") # spark sql查询
# DataFrame 中每个原色都是一行记录,包含name和age两个字段,分别用p.name p.age 来获取
personsRDD=personsDf.rdd.map(lambda p:"Name:"+p.name+","+"Age:"+str(p.age)) #再转化为rdd用于显示
personsRDD.foreach(print)
使用编程模式定义RDD模式:当无法提前获取数据结构时,采用编程的方式定义RDD模式
步骤
- 制作表头
- 制作表中的记录
- 把表头和表中的记录拼装在一起
from pyspark.sql.types import *
from pysqark.sql import Row
生成表头(表的字段)
schemaString="name age"
fileds=[StructFiled(filed_name,StringType(),True) for field_name in schemaString.split(" ")] # structFiled 用于描述字段信息,字段名称,类型,是否为空
schema=StructType(fields) # 生成表头
制作表中的记录 : 也即是将数据行记录生成row对象
lines=spark.sparkContext.textFile("file:///usr/local/spark/example/people.txt")
parts=lines.map(lambda x x.split(",")) # rdd
people=parts.map(lambda p : Row(p[0],p[1].strip())) # rdd 元素变成row对象
把表头和表中的记录拼装在一起
schemaPeople=spark.createDataFrame(people,schema)
# 注册一个临时表供下面查询使用
schemaPeople.createOrReplaceTempView("people")
results=spark.sql("SELECT name ,age FROM people") # dataframe
results.show()
# 读取mysql中的数据 通过JDBC连结MySQL数据库
jdbcDF=spark.read.format('jdbc') \
.option("driver","com.mysql.jdbc.Driver") \ # 指定驱动
.option("url",'jdbc:mysql://localhost:3306/spark') \ #要访问的数据地址
.option("dbtable","student") \ # 表明
.option("user","root") \
.option("password","123456").load() # 密码
jdbcDF.show()
向mysql中写入数据
from pyspark.sql import Row
from pyspark.sql.types import *
from pyspark import SparkContext,SparkConf
from pyspark.sql import SparkSession
spark = SparkSession.builder.config(conf=SparkConf()).getOrCreate() # 生成一个spark sql 对象
#下面设置模式信息
schema=StructType([StructFiled("id",IntegerType(),True),\
StructFiled("name",StringType(),True),\
StructFiled("gender",Stringype(),True),
StructFiled("age",IntegerType(),True)])
# 下面设置两条数据,表示两个学生的信息
studentRDD=spark.saprkContext.parallelize(["3 Rongcheng M 26","4 Guanhua M 27"]).map(lambda x:x.split(" "))
# 下面创建Row对象,每个Row对象都是rowRDD中的一行
rowRDD=studentRDD.map(lambda p :Row(int(p[0].strip(),p[1].strip(),p[2].strip(),int(p[3].strip())))
# 建立起row对象和模式之间的对应关系,也即是把数据和模式对应起来
studentDF=spark.createDataFrame(rowRDD,schema) # 创建一个dataframe
#将上面的dataframe 写入数据库
prop={}
prop["user"]="root"
prop["password"]='123456'
prop["driver"]="com.mysql/jdbc.Driver"
studentDF.write.jdbc("jdbc:mysql://localhost:3306/spark","student","append",prop) # append:表示追加模式
编写sparkstreaming程序固定的步骤:
- 创建输入DStream ------定义输入源
- DStream应用(转换操作和输出操作)定义--------》流计算
- 开始接收数据和处理流程 streamingContext.start()
- 等待处理结束:streamingContext.awaitTermination()
- 手动结束流计算流程 streamingContext.stop()
要运行一个Spark Streaming程序,就需要先生成一个StreamingContext对象,它是Spark Streaming程序的主入口----->可以从一个SparkConf对象创建一个StreamingContext对象 ------> 在pyspark中的创建方法进入pyspark以后,就可以获得一个默认的SparkContext对象,也就是sc、
创建StreamingContext对象(交互环境)
from pyspark.streaming import StreamingContext
ssc=StreamingContext(sc,1)
编写独立的Spark Streaming程序
from pyspark import SparkContext ,SparkConf
from pyspark.streaming import StreamingContext
conf = SparkConf()
conf.setAppName("TestDStream")
conf.setMaster('local[2]')
sc=SparkContext(conf=conf)
ssc=StreamingContext(sc,1)
基本数据源:文件流,套接字流 RDD队列流
创建文件流
cd /usr/local/spark/mycode
mkdir streaming
cd streaming
mkdir logfile
cd logfile
交互式环境下 对文件流处理进行词频统计,会将出现新文件分别处理,流处理问就
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
ssc=StreamingContext(sc,10) #10表示每隔10秒计算一次
lines=ssc.textFileStream('file:///usr/local/spark/mycode/streaming/logfile') # 定义文件流对象,参数为监控目录
words=lines.flatMap(lambda line:line.split('')) # 流计算过程
wordCounts=word.map(lambda x : (x,1)).reduceByKey(lambda a,b:a+b)
wordCounts.pprint() # 格式化输出
ssc.start()
ssc.awaitTermination()
采用独程序方式创建文件流
cd /usr/local/spark/mycode
cd streaming
cd logfile
vim FileStreaming.py
from pyspark import SparkContext,SparkConf
from pyspark.streaming import StreamingContext
conf=SparkConf().setAppNmae("TestDstream").setMaster("local[2]')
sc=SparkContext(conf=conf) # 创建sparkcontext对象
ssc=StreamingContext(sc,10) # 生成一个spark StreamingContext 对象10表示每隔10秒计算一次
lines=ssc.textFileStream('file:///usr/local/spark/mycode/streaming/logfile') # 定义文件流对象,参数为监控目录
words=lines.flatMap(lambda line:line.split('')) # 流计算过程
wordCounts=word.map(lambda x : (x,1)).reduceByKey(lambda a,b:a+b)
wordCounts.pprint() # 格式化输出
ssc.start()
ssc.awaitTermination()
执行程序
cd /usr/local/spark/mycode/streaming/logfile/
/usr/local/spark/bin/spark-submit FileStreaming.py
套接字流作为数据源实现spark streaming(使用NC程序产生数据)
socket工作原理:包含TCP客户端和TCP服务器端,客户端向服务端发起请求,超产生数据流
cd /usr/local/spark/mycode/streaming
mkdir socket
cd socket
vim NetworkWordCount.py 客户端的角色
from __future__ import print_function
import sys
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
if __name__ =="__main__":
if len(sys.argv)!=3:
print("Usage:NetworkWordCount.py <hostname><port>",file=sys.stderr)
exit(-1)
sc=SparkContext(appName="PythonStreamingNetworkWordCount")
ssc=StreamingContext(sc,1)
lines=ssc.socketTextStream(sys.argv[1],int(sys.argv[2]))
counts=lines.flatMap(lambda line:line.split(" ")).map(lambda word :(word,1)).reduceByKey(lambda a,b:a+b)
counts.pprint()
ssc.start()
ssc.awaitTermination()
再新建一个终端,执行如下代码启动流计算
建立nc服务端,linux自带的nc $ nc -lk 9999
cd /usr/local/spark/mycode/streaming/socket
/usr/local/spark/bin/spark-submit NetworkWordCount.py localhost 9999 # 启动客户端
在nc窗口中随意输入单词,监听窗口自动获取单词数据流信息
使用Socket编程实现自定义数据源
cd /usr/local/spark/mycode/streaming/socket
vim DataSourceSocket.py
import scoket
server=socket.socket() #生成socket对像
server.bind(("localhost",9999)) # 绑定机器ip和端口
server.listen(1) # 绑定监听端口
while 1:
print("I am waiting the connect...") # 为方便识别,打印一个我在等待
# accept进入阻塞状态
conn,addr=server.accept() # 这里用两个值接受,因为连结上之后使用的是客户端发来的请求的这个实例,所以下面的传输要使用coon实例操作
print("Connect success!Connection is from %s" %addr[0])
#打印正在发送数据
print("sending data...")
conn.send("I love hadoop I love spark hadoop is good spark is fast".encode()) # 服务端发信息到客户端
conn.close()
print("Connection is broken.")
启动DataSourceSocket程序
cd /usr/local/spark/mycode/streaming/socket
/usr/local/spark/bin/spark-submit DataSourceSocket.py
启动客户端,即NetworkWordCount 程序
/usr/local/spark/mycode/streaming/socket
/usr/local/spark/bin/spark-submit NetworkWordCount.py localhost 9999
RDD队列流
可以使用 streamingContext.queueStream(queueofRDD) 创建基于RDD队列的DStream
RDDQueueStream.py 每个一秒创建一个RDD,塞到队列中,每个两秒对数据进行处理
import time
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
if __name__=="__main__":
sc=SparkContext(appName="PythonStreamingQueueStream")
ssc =streamingCobtext(sc,2)
#创建一个队列,通过该队列可以把RDD推给一个RDD队列流
rddQueue=[]
for i in range(5):
rddQueue+=[ssc.sparkContext.parallelize([j for j in range(1,1001)],10)] # 10代表分区数
time.sleep(1)
#创建一个RDD队列流
inputStream=ssc.queueStream(rddQueue)
mappedStream=inputStrean.map(lambda x :(x%10,1))
reduceStream=mappedStream.reduceByKey(lambda a,b:a+b)
reducedStream.pprint()
ssc.start()
ssc.stop(stopSparkContext=True,stopGraceFully=True)
高级数据源kafka
:高吞吐量的分布式发布订阅消息系统,可以同时满足在线实时处理和批量离线处理
组件 broker
(每个服务器) topic
(主题)每条 发布到kafka
集群的消息都有一个类别,这个类别被称为topic(物理上不同的Topic的消息分别存放,逻辑上一个Topic
的消息,虽然保存于一个或多个broker
上,但是用户只需要指定消息的Topic既可生产或者消费数据而不必关心数据存放在何处)partition
Produer
:数据的生产者
消费者 Consumer Group
- 每个Consumer 只属于某个Consumer Group
- 若不指定group name 则属于默认的group
结构:kafka的运行依赖于Zookeeper
.Topic,Consumer,Prtiton,Broker等注册信息都存放在zookeeper中
kafkaWordCount.py
from __future__ import print_function
import sys
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
from pyspark.streaming.kafka import KafkaUtils
if __name__=="__main__":
if len(sys.argv)!=3:
print("Usage:KafkaWordCount.py<zk><topic>",file=sys.stderr)
exit(-1)
sc=SparkContext(appName="PythonStreamingKafkaWordCount")
ssc=StreamingContext(sc,1)
zkQuorum,topic=sys.argv[1:] # zookeeper地址,和topic。 取数据的地址
kvs=KafkaUtils.createStream(ssc,zkQuorum,"spark-streaming-consumer",{topic:1}) # 构建kafka数据源对象
lines=kvs.map(lambda x:x[1])
counts=line.flatMap(lambda line:line.split(" ")).map(lambda word:(word,1)).reduceByKey(lambda a,b:a+b)
counts.pprint()
ssc.start()
ssc.awaitTermination()
cd /usr/local/spark/mycode/streaming/kafka/
/usr/local/spark/bin/spark-submit ./KafkaWordCount.py localhost:2181 wordsendertest (topic数据源)
状态转换操作(滑动窗口转换操作)
1.滑动窗口转换操作
事先设定一个滑动窗口的长度(也就是窗口的持续时间)
设定滑动窗口的时间间隔,让窗口按照指定的时间间隔在源DStream上滑动
每次窗口停放的位置,都会有一部分DStream被框入窗口内,形成一个小段的DStream
可以启动对这个小段DStream的计算
参数(windowLength,slideInterval)# 滑动窗口大小,滑动窗口间隔
reduceByKeyAndWindow(func,invFunc,windowLength,slideInterval,[numTasks])
更高效的reduceByKeyAndWindow,每个窗口的reduce值,是基于先前窗口的reduce值进行增量计算得到的;它会对进入的滑动窗口的
新数据进行reduce操作,并对离开窗口的老数据进行”逆向reduce“操作。但是,只能用于”可逆reduce函数“,即那些reduce函数都有一个
对应的”逆向reduce函数“,以invFun参数传入
WindowedNetworkWordCount.py
from __future__ import print_function
import sys
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
if __name__=="__main__":
if len(sys.argv)!=3:
print("Usage:WindowedNetworkWordCount.py<hostname><port>",file=sys.stderr)
exit(-1)
sc=SparkContext(appName="PythonStreamingWindowedNetworkWordCount")
ssc=StreamingContext(sc,10)
ssc.checkpoint("file:///usr/local/spark/mycode/streaming/socket/checkpoint") # 保存数据,以防丢失
lines=ssc.socketTextStream(sys.argv[1],int(sys.argv[2]))
counts=lines.flatMap(lambda line:line.split(" ")).map(lambda word:(word,1)) \
.reduceByKeyAndWindow(lambda x,y:x+y,lambda x,y:x-y,30,10) # 逆函数的作用加x+y后形成的数据再一次x-y后回复源数据,使得新窗口内的函数依然为原始的数据
# 即是将离开窗口的数据减去,将新加入窗口数据加进来,实现数据的增量操作
counts.pprint()
ssc.start()
ssc.awaitTermination()
2.updateStateByKey操作 在跨批次之间维护状态
对于有状态转换操作而言,本批次的词频统计,会在之前批次的词频统计结果的基础上进行不断累加。所以,最终统计得到的词频,
是所有批次的单词的总的词频统计结果。
历史状态累加
from __future__ import print_function
import sys
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
if __name__=="__main__":
if len(sys.argv)!=3:
print("Usage:WindowedNetworkWordCount.py<hostname><port>",file=sys.stderr)
exit(-1)
sc=SparkContext(appName="PythonStreamingWindowedNetworkWordCount")
ssc=StreamingContext(sc,1)
ssc.checkpoint("file:///usr/local/spark/mycode/streaming/socket/checkpoint") # 保存数据,以防丢失
# RDD with initial state(key,value) pairs
initialStateRDD=sc.parallelize([(u"hello",1),(u"word",1])
def updataFunc(new_values,last_sum):
return sum(new_values)+(last_sum or 0)
lines=ssc.socketTextStream(sys.argv[1],int(sys.argv[2]))
running_counts=line.flatMap(lambda line:line.split(" ")).map(lambda word:(word,1)).updataStateByKey(updataFunc,initialRDD=initialStateRDD) # 有状态转换
running_counts.pprint()
ssc.start()
ssc.awaitTermination()
输出操作:
1.把Dstream输出到文本文件中
2. 把DStream写入到mySQL数据库中
NetworkCountStatefulText.py
from __future__ import print_function
import sys
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
if __name__=="__main__":
if len(sys.argv)!=3:
print("Usage:NetworkCountStatefulText.py<hostname><port>",file=sys.stderr)
exit(-1)
sc=SparkContext(appName="NetworkCountStatefulText")
ssc=StreamingContext(sc,1)
ssc.checkpoint("file:///usr/local/spark/mycode/stateful") # 保存数据,以防丢失
# RDD with initial state(key,value) pairs
initialStateRDD=sc.parallelize([(u"hello",1),(u"word",1])
def updataFunc(new_values,last_sum):
return sum(new_values)+(last_sum or 0)
lines=ssc.socketTextStream(sys.argv[1],int(sys.argv[2]))
running_counts=line.flatMap(lambda line:line.split(" ")).map(lambda word:(word,1)).updataStateByKey(updataFunc,initialRDD=initialStateRDD) # 有状态转换
running_counts.saveAsTextFiles("file:///usr/local/spark/mycode/streaming/stateful/output")
running_counts.pprint()
ssc.start()
ssc.awaitTermination()
输出到mysql数据库中
from __future__ import print_function
import sys
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
if __name__=="__main__":
if len(sys.argv)!=3:
print("Usage:NetworkCountStatefulText.py<hostname><port>",file=sys.stderr)
exit(-1)
sc=SparkContext(appName="NetworkCountStatefulText")
ssc=StreamingContext(sc,1)
ssc.checkpoint("file:///usr/local/spark/mycode/stateful") # 保存数据,以防丢失
# RDD with initial state(key,value) pairs
initialStateRDD=sc.parallelize([(u"hello",1),(u"word",1])
def updataFunc(new_values,last_sum):
return sum(new_values)+(last_sum or 0)
lines=ssc.socketTextStream(sys.argv[1],int(sys.argv[2]))
running_counts=line.flatMap(lambda line:line.split(" ")).map(lambda word:(word,1)).updataStateByKey(updataFunc,initialRDD=initialStateRDD) # 有状态转换
running_counts.pprint()
def dbfunc(records): # 将partition 中的每一条计算插入数据库中
db=pymysql.connect("localhost","root","123456",'spark')
cursor=db.cursor() # 声明一个指针
def doinsert(p): # 插入具体的键值对
sql="insert into wordcount(word,count) values ("%s","%s")" % (str(p[0]),str(p[1]))
try:
cursor.execute(sql) # 生成SQL语句
db.commit()
except:
db.rollback()
for item in records:
doinsert(item)
def func(rdd):
repartitionedRDD=rdd.repartition(3)
repartitionedRDD.foreachParttion(dbfunc) #foreachPartition 为每个rdd中的键值元素,每条记录传给dbfunc进行处理
running_counts.foreachRDD(func)
ssc.start()
ssc.awaitTermination()
Structured Sreaming: 相应比spark streaming更快
概述:Structured Streaming的关键思想是将实时数据流视为一张正在不断添加数据的表。可以把流计算等同于在一个静态表上的批处理查询,
Spark会在不断添加数据的无界输入表上运行计算,并进行增量查询。在无界表上对输入的查询将生成结果表,系统每隔一定的周期会触发对无界表的计算并更新结果表。
Structured Streaming 默认使用微批处理执行模型。这意味着spark流计算引擎会定期检查流数据源,并对自上一批结束后到达的新数据执行流量查询
spark streaming处理的数据抽象是Dstream(本质上是一系列的RDD),Structured Streaming 采用的数据抽象是DataFrame
from pyspark.sql import SparkSession
from pyspark.sql.functions import split # 拆分字符串
from pyspark.sql .functions import explode # 展开数组内的所有单词
# 创建sparkSession对像
if __name__=="__main__":
spark=SparkSession.builder.appName("StructuredNetworkwordCount").getOrCreate()
spark.sparkContext.setLogLevel("WARN")
# 创建一个输入数据源
lines=spark.readSteam.format("socket").option("host","localhost").option('port',9999).load()
# 定义流计算的过程
words=lines.select(explodes(split(lines.value," ")).alias("word")) #alias 定义单词列
word.Counts=words.groupBy("word").count()
#执行流计算
query =wordCounts.writeStream.outputMode("complete").format("console")\ # console 输出到控制台
.trigger(processingTime="8 seconds").start()
query.awaitTermination()
数据源:
file源
:捕捉文件变化,以文件流的形式读取某个目录中的文件。
spark mllib
基于RDD
的数据抽象,包含基于RDD的原始算法API
spark.ml
基于DataFrame
的数据抽象,则提供基于dataframe高层次的api,可以用来构建机器学习工作流PipLine
机器学习流水线:
transformer
(转换器)
比如一个model
就是一个Transformer
,它可以把一个不包含预测标签的测试数据集dataframe
打上标签,转化成一个包含预测标签的dataframe
。技术上,Transormer
实现了一个方法transform()
,它通过附加一个或多个列将一个DataFrame转换为另一个dataFrame
Estimator评估器(本质上理解为算法)
它是学习算法或者训练数据上的训练方法的概念抽象,在PipLine
里通常被用来操作Dataframe
数据并生成一个Transformer
。
从技术上讲,Estimator
实现了一个方法fit()
,它接受一个Dataframe
并产生一个转换器。比如,一个随机森林算法就是一个Estimator
,它可以调用fit()
,通过训练户据特征数据而得到一个随机森林模型。
Parameter
被用来设置Transformer
或者Estimator
的参数。现在,所有的转换器和估计器可共享用于指定参数的公共API,ParamMap
是一组(参数,值)对
构建PIpline
1.定义Pipline
中的各个流水线阶段PiplineStage
---->转换器或评估器
2. 转换器和评估器有序地组织起来构建成PipeLine
pipline=Pipline(stages=[stage1,stage2,stage3])
# 把训练数据集作为输入参数,调用fit()方法,返回一个PiplineModel类实例,被用来预测测试集合的标签
#创建sparkSeeion对象
from pyspark.sql import SparkSession
spark=SparkSession.builder.master("local").appName("WordCount").getOrCreate()
from pyspark.ml import Pipline
from pyspark.ml.classification import LogisticRegression
from pysaprk.ml.feature import HashingTF,Tokenizer
# prepare training document from a list of (id,text,lable) tuples
training=spark.createDataFrame([
(0," a b c spark",1.0),
(1,"a h",0.0),
(2,"spark is hg",1.0),
(3,"hadoop g h k",'0.0')]
,["id","text","label"])
- 定义Pipiline中的各个流水线阶段PipelineStage
转换器
tokenizer=Tokenizer(inputCol="text",outputCol="words") # 新生成列命名为words
转换器
hashingTF=HashingTF(inputCol=tokenizer.getOutputCol(),outputCol="features") # 转化为词向量,生成新的一列,命名为features
评估器
lr=LogisticRegression(maxIter=10,regParam=0.001)
按照处理逻辑有序地组织PipelineStages,创建Pipeline
pipeline=Pipeline(stages=[tokenizer,hashingTF,lr])
#现在构建的Pipline
本质上是一个Estimator
,在它的fit()
方法运行之后,它将产生一个PipelineModel
,它是一个Transformer
。
model=pipeline.fit(training)
#可以看到,model
的类型是一个PipelineModel
,这个流水线模型将在测试数据的时候使用。
构建测试数据
training=spark.createDataFrame([
(4," a b c spark"),
(5,"a h"),
(6,"spark is hg"),
(7,"hadoop g h k")]
,["id","text"])
生成预测结果
prediction=model.transform(test)
selected=prediction.select("id","text","probability","prediction")
for row in selected.collect():
rid,text,prob,prediction=row
print("(%d,%s)-->prob=%s,prediction=%f"%(rid,text,str(prob),preduction))
抽取特征:
TF: Hashing TF
是一个Transformer
, IDF
是一个Estimator
,在一个数据集上应用它的fit()
方法,产生一个IDFModel
from pyspark.ml.feature import HashingTF,IDF,Tokenizer
#创建一个简单的DataFrame,每一个句子代表一个文档
sentenceData=spark.createDataFrame([(0,"I heard about Spark and I love Spark"), # 生成一个二维表
(1,"I wish Java could use case classes")]).toDF("label',"sentence") #
tokenizer=Tokenizer(inputCol="sentence",outputCol="words") # 增加一列words
wordsData=tokenizer.transform(sentenceData)
wordsData.show()
#利用HashingTF的transform()方法把句子哈希成特征数量
hashingTF=HashingTF(inputCol="words",outputCol="rawFeatures",numFeatures=2000)
featurizedData=hashingTF.transform(wordsData)
featurizedData.select("words","features").show(trancate=False)
idf=IDF(inputCol="rawFeatures",outputCol="features")
idfModel=idf.fit(featurizedData)
rescaledData=idfModel.transform(featurizedData)
rescaledData.select("features","label").show()