:::tips
此处学习过程为 _JAVA _版本,但API基本相同(只存在部分差异,比如rdd切分操作partition在python中为parallelize),替换即可,不影响学习。
代码块中的代码均为 _pyspark _版本,可直接使用。
:::
RDD编程基础
什么是RDD:
概念:一个弹性分布式数据集
RDD的特征 :分区列表
· 计算函数:用于计算每个分区的函数
· 依赖关系:对其他RDD的依赖关系表
· 分区器:可选的,键值RDD的分区器
· 首选位置:可选的,计算每个拆分的首选位置列表(例如HDFS)
Wordcount中的RDD五大属性
RDD的创建:
数据来源
RDD中的数据可以来自两个地方:本地集合或外部数据源
RDD的构建方法(Java/python)
模拟创建来验证代码:
创建结果:
小结:
RDD的主要操作:
操作类型
tips
如:先让小明去喊小王,小明不动;再让小明喊小张,小明还不动;这个时候给小明发送:行动指令(action),小明开始行动
在这里称之为惰性机制。
基本算子/操作/方法/API(java/python)
map
faltMap
filter
foreach
saveAsTextFile
groupByKey
reduceByKey
…
输出:
转换操作(python实现):
1.0 传递文件函数textFile()加载数据
lines = sc.textFile("file:///home/master/test.txt") ##换成自己的位置
lines.foreach(print) ##打印每个元素
lines=sc.textFile("hdfs://localhost:9000/zcw666/test6.txt")
lines=sc.textFile("/zcw666/test6.txt")
lines=sc.textFile("test6.txt") ##只在路径内起作用!
2.0 .parallelize()创建RDD函数
array=[1,2,3,4,5,6]
rdd = sc.parallelize(array)
rdd.foreach(print)
*此处结果为乱序(哈希存放?应该是)
通过parallelize操作进行分区,分区为数组
3.0 filter筛选函数
lines=sc.textFile("file:///home/master/test.txt")
linesWithSpark = lines.filter(lambda line:"hello" in line)
linesWithSpark.foreach(print)
一个筛选函数,筛选符合条件的值并返回到一个新的数据集
4.0几种Map映射函数
data=[1,2,3,4,5]
rdd1=sc.parallelize(data)
rdd2=rdd1.map(lambda x:x+10)
rdd2.foreach(print)
与python中的map映射函数类似,用于对rdd进行操作
lines=sc.textFile("file:///home/master/test.txt")
words=lines.map(lambda line:line.split(""))
words=lines.map(lambda line:line.split(" "))
words.foreach(print)
lines=sc.textFile("file:///home/master/test.txt")
words=lines.flatMap(lambda line:line.split(" "))
words.foreach(print)
等同于先map再flat拍平
5.0聚合函数reduceByKey(func)
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words1=words.reduceByKey(lambda a,b:a+b)
words1.foreach(print)
6.0 返回分组函数groupByKey()
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words1=words.groupByKey()
words1.foreach(print)
行动操作:
:::tips
count() 返回数据集中的元素个数
collect() 以数组的形式返回数据集中的所有元素
first() 返回数据集中的第一个元素
take(n) 以数据的形式返回前n个元素
reduce(func) 通过函数func(输入两个参数返回一个值)聚合数据集中的元素
foreach(func) 将数据集中的元素传递到函数func中运行
:::
rdd= sc.parallelize([1,2,3,4,5,6])
rdd.count()
rdd.first()
rdd.collect()
rdd.take(3)
rdd.reduce(lambda a,b:a+b)
rdd.foreach(lambda elem:print(elem))
RDD的持久化(python):
持久化(缓存)可以用来避免惰性机制的迭代计算开销过大的问题,原本每一次进行action便会重新进行一次transformation,加了持久化后第一次计算后便会在一段时间内不再需要重新transformation。
具体使用方法为:使用persist()方法
一般而言使用cache()方法就会调用persist()
list=['hadoop','hello','hive']
rdd= sc.parallelize(list)
rdd.cache()
print(rdd.count())
print(','.join(rdd.collect()))
RDD的分区操作java :
map:表示针对分区中的每一个数据进行操作
mapPartions: 表示针对每个分区进行操作
foreach 同理
重分区函数
聚合函数
注意这里只进行了Action,没有进行transform
关联函数
排序函数
sortBy //可以根据其他字段进行排序的RDD
sortByKey //返回一个根据key排序的RDD
---------------------------------------------------------------------------------------------------------
*top //按照RDD中的数据采用降序的方式排序,如果是键值对按照Key降序排序
*只有在结果数字很小的时候能够使用top(返回的是数组)
//需求:求结果的TOP3
RDD的分区操作python :
分区的原因:
不分区进行数据处理容易造成数据混洗,网络传输开销大,分区可大大减少数据混洗的情况。
分区的原则:
1.尽量等于集群中的CPU核心(core)数目
对于任意的spark部署模式都可以通过设置:spark.default.parallelism 这个参数值,来配置默认的分区数目。
设置分区的个数:
1.创建RDD的时候手动设定分区个数:
sc.textFile(path,partitionNum)
list=[1,2,3,4,5,6]
rdd=sc.parallelize(list,2) //设置两个分区
如果不设置分区数量
:::tips
对于textFile()会默认为min(defailtParallelism,2)defailtParallelism这个值对应spark.default.parallelism
:::
:::tips
对于parallelize()会默认spark.default.parallelism 这个参数值
:::
2.重新设置分区个数repartition:
使用repartition方法
data=sc.parallelize([1,2,3,4,5],2)
len(data.glom().collect()) //显示data这个rdd分区数量
rdd=data.repartition(1) //重新设置这个分区数量
len(rdd.glom().collect()) //再次显示rdd数量
自定义分区方法(拓展):
spark自带HashParatitioner(哈希分区)与RangeParatitioner(区域分区),能够满足大多数的场景。同时也支持自定义分区。(自学)
RDD基础示例
文件内容:
hello world
hello spark
hello hadoop
hahahhahahahah
wojiuzhidaonihuifanyizhejuhuade
Anallve.cityz-19
lines=sc.textFile("file:///home/master/test.txt")
str=lines.flatMap(lambda line :line.split(' '))
dir=str.map(lambda a:(a,1))
wordcount=dir.reduceByKey(lambda a,b:a+b)
wordcount.foreach(print)
结果:
RDD键值对
键值对RDD的创建
键值对的创建主要有两种方式:
1.文件中加载生成RDD
2.通过并行集合(列表)创建RDD
从文件中加载
首先使用file()方法从文件中加载数据,然后使用map()函数转换得到相应的键值对RDD
lines=sc.textFile("file:///home/master/test.txt")
pairRDD=lines.flatMap(lambda line :line.split(" ")).map(lambda a:(a,1)).foreach(print)
通过并行集合创建RDD
list=['a','b','n','m','j','c']
rdd=sc.parallelize(list)
pairRDD=rdd.map(lambda word:(word,1)).foreach(print)
常用的键值对转换操作
1.0 聚合函数reduceByKey(func)
(同上文聚合函数5.0)
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words1=words.reduceByKey(lambda a,b:a+b)
words1.foreach(print)
2.0 分组函数groupByKey()
(同上文分组函数6.0)
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words1=words.groupByKey()
words1.foreach(print)
reduceByKey可以聚合操作,但groupByKey不可以,需要加上map()函数才可以达到根reduceByKey一样的效果。两者本就殊途同归
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words1=words.groupByKey()
words2=words1.map(lambda x:(x[0],sum(x[1])))
words2.foreach(print)
3.0 keys,valus 返回函数
返回关键字key
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words2.keys().foreach(print)
返回关键字value
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words2.values().foreach(print)
4.0 sortByKey 排序
函数会根据key排序
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words.sortByKey().foreach(print)
5.0 sortBy排序
函数可以根据其他字段排序
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words.reduceByKey(lambda a,b:a+b).sortByKey(false).collect()
注意这里必须采用collcet()收集才能正常排序,单独使用sortByKey容易出错!上面4.0同理,可以看出来并不是升序的
6.0mapValues(func)
对键值对的value进行map函数操作
words= sc.parallelize([("a",1),("b",1),('a',1),("c",2),('b',3)])
words1=words.mapValues(lambda x:x**2)
words1.foreach(print)
7.0 join
join表示连接,对给定的两个键值(K,V1)和(K,V2)对进行连接得到(k,(V1,V2))类型的数据集。
words= sc.parallelize([("a","10.30"),("b",1),('d',1),("c",2),('b',3)])
words1= sc.parallelize([("a","birthday"),("b",1),('d',1),("c",2),('b',3)])
word=words.join(words1)
word.foreach(print)
8.0 combineByKey
combineByKey(createCombiner,mergeValue,mergeCombiners,partitioner,mapSideCombine)
:::warning
- createCombiner:在第一次遇到key时创建组合器函数,将RDD数据集中的V类型转化为C类型值(V=>C);
- mergeValue:合并值函数,再次遇到相同的Key时,将createCombiner传出的C值类型与这次传人的V值进行合并成一个C值类型
- mergeCombiners:合并组合器函数,将C类型值两两合并成一个C值类型;
- partitioner:使用已有的或者自定义的分区函数,默认为HashPartitioner
- mapSideCombine:是否在map端进行Combine操作,默认为Ture
:::
from pyspark import SparkConf,SparkContext
conf = SparkConf().setMaster("local").setAppName("Combine")
sc=SparkContext(conf=conf)
data=sc.parallelize([('company1',57),('company1',30),('company2',55),('company1',15),\
('company3',30),('company2',11),('company2',18),('company3',62),],3)
res=data.combineByKey(\
lambda income:(income,1),\
lambda acc,income:(acc[0]+income,acc[1]+1),\
lambda acc1,acc2:(acc1[0]+acc2[0],acc1[1]+acc1[1])).\
map(lambda x:(x[0],x[1][0],x[1][1],x[1][0]/x[1][1]))
res.repartition(1).saveAsTextFile('file:///home/master/combineresult')
数据读写:
1.文件数据的读写
1.1本地文件的读写(textFile)
textfile=sc.textFile("file:///home/master/test.txt")
textfile.first()
1.2本地文件的写()
textfile=sc.textFile("file:///home/master/test.txt")
textfile.saveAsTextFile("file:///home/master/test1")
注:写入的是RDD类型的文件
这里的rdd默认以需要的内核数打开(我这里为2),实际情况可能需要发生改变,可以尝试使用:
pyspark --master local[1]
textfile=sc.textFile("file:///home/master/test.txt")
textfile.saveAsTextFile("file:///home/master/test2")
来限制内核数量
2.HDFS的读写操作(textFile)
>>> textfile=sc.textFile("hdfs://zcw666/test.txt")
>>> textfile=sc.textFile("/zcw666/test.txt")
>>> textfile=sc.textFile("test.txt")
>>> textfile.saveAsTextFile("test11")
>>> textfile=sc.textFile("/zcw666/test1.txt")
>>> textfile.saveAsTextFile("/zcw666/new_test1")
后续补充学习
RDD.count() → int 元素数量计算
lines.filter(lambda line :(len(line.strip())>0) 去掉空行的语句
##filter:过滤器
if __namea__=='__main__':
main()