弹性分布式数据集(Resilient Distributed Dataset),是Spark的核心,属于一种分布式的内存系统数据集应用,能与其他系统兼容,导入外部存储系统的数据集,如HDFS、HBase等
RDD的特性
1、RDD的三种基本运算
Lineage机制具备容错的特性
RDD本身具有Linear机制。记录每个RDD与其父代RDD之间的关联,还会记录通过什么操作才由父代RDD得到该RDD的信息。
RDD本身的immutable(不可变)特性,再加上Linearage机制,使得Spark具备容错的特性。如果某节点的机器发生故障,那么存储于这个节点上的RDD损毁后就会重新执行一连串的**“转换”**命令,产生新的输出数据。
2、转换运算
进入pyspark
创建intRDD(转换运算,不会立即执行)
intRDD = sc.parallelize([3, 1, 2, 5, 5])
intRDD转换为List(动作运算,立即执行)
intRDD.collect()
创建stringRDD
stringRDD = sc.parallelize(["A","B","C","D","E"])
map运算
map运算可以通过传入的函数将每一个元素经过函数运算产生另外一个RDD,RDD通过传入的函数addOne将每一个元素加1,从而产生另一个RDD。
map运算中,可以使用两种语句:具名函数和匿名函数
使用具名函数
def addOne(x):
return (x+1)
将addOne作为参数传入map函数
intRDD.map(addOne).collect()
使用匿名函数
此方法更为简洁
intRDD.map(lambda x : x + 1).collect()
字符串运算
stringRDD.map(lambda x : "letter:" + x).collect()
filter运算
数字运算
intRDD.filter(lambda x:x<3).collect()
字符串运算
stringRDD.filter(lambda x : "C" in x).collect()
distinct运算
intRDD.distinct().collect()
randomSplit运算
randomSplit运算可以将整个集合元素以随机数的方式按照比例分为多个RDD
以随机数的方式按照4:6的比例分割为两个RDD
sRDD = intRDD.randomSplit([0.4,0.6])
分开的两个RDD
sRDD[0].collect()
sRDD[1].collect()
groupBy运算
groupBy可以按照传入的函数规则将数据分为多个list
将集合分为奇数与偶数
gRDD = intRDD.groupBy(
lambda x:"even" if x % 2 == 0 else "odd"
).collect()
3、多个RDD转换运算
i1 = sc.parallelize([3, 1, 2, 5, 5])
i2 = sc.parallelize([5, 6])
i3 = sc.parallelize([2, 7])
union并集运算
i1.union(i2).union(i3).collect()
intersection交集运算
i1.intersection(i2).collect()
subtract差集运算
i1.subtract(i2).collect()
cartesian笛卡尔乘积运算
i1.cartesian(i2).collect()
4、基本“动作”运算
读取元素
i1.first()
i1.take(2)
i1.takeOrdered(3) # 从小到大排序,取出前三项
i1.takeOrdered(3, key=lambda x:-x) # 从小到大排序,取出前三项
统计
i1.stats()
5、RDD Key-Value 基本“转换”运算
RDD支持Key-Value运算,Key-Value运算也是MapReduce的基础
kv1 = sc.parallelize([(3,4),(3,6),(5,6)])
使用filter筛选出key<5
kv1.filter(lambda keyValue:keyValue[0]<5).collect()
同理可筛选出value<5的元素
mapValues运算
mapValues运算可对RDD内每一组(Key,Value)进行运算,并产生另一个RDD
将Value的每一个值进行平方运算
kv1.mapValues(lambda x:x*x).collect()
sortByKey运算
按照key排序,默认参数为True,从小到大排序
kv1.sortByKey(ascending=True).collect()
reduceByKey
reduceByKey按照Key进行reduce运算
运算过程:
找到相同的key值,将他们的value按照规则合并,不同的key值的元素不进行操作
相同key值的相加
kv1.reduceByKey(lambda x,y:x+y).collect()
6、多个RDD Key-Value“转换运算”
kv1 = sc.parallelize([(3,4),(3,6),(5,6)])
kv2 = sc.parallelize([(3,8)])
join运算
将两个RDD按照相同的key值组合
kv1.join(kv2).collect()
leftOuterJoin运算
左连接
kv1.leftOuterJoin(kv2).collect()
rightOuterJoin运算
右连接
kv1.rightOuterJoin(kv2).collect()
subtractByKey运算
删除相同key值的数据
kv1.subtractByKey(kv2).collect()
Key-Value“动作”运算
读取元素
kv1.first()
kv1.take(2)
kvFirst=kv1.first()
kvFirst[0]
计算Key的项数
kv1.countByKey()
lookup运算,
查找对应Key值的Value
kv1.lookup(3)
collectAsMap创建字典
kv = kv1.collectAsMap()
kv
type(kv)
7、共享变量
共享变量可用于节省内存与运行时间,提升并行处理时的执行效率,共享变量包括广播变量和累加器
广播变量
在进行操作时,我们会发现有些操作会产生许多RDD,这样会耗费很多内存和时间,如以下例子
为解决这个问题,就必须使用Broadcast广播变量
广播变量使用规则
先创建好示例数据
kv = sc.parallelize([(1,"apple"),(2,"orange"),(3,"banana"),(4,"grape")])
kvMap = kv.collectAsMap()
1、使用SparkContext.broadcast([初始值])创建
bckvMap = sc.broadcast(kvMap)
2、使用.value方法读取广播变量的值
kvID = sc.parallelize([2,4,1,3])
values = kvID.map(lambda x:bckvMap.value[x]).collect()
values
3、Broadcast广播变量被创建后不能修改
执行结果:
在进行并行处理中,bckvMap广播变量会传送到Worker Node机器,并且储存到内存中,后续在此Work Node都可以使用bckvMap执行转换,这样可以节省很多内存与传送时间
accumulator累加器
计算总和是MapReduce常用的运算。为了方便并行处理,Spark提供了累加器共享变量
使用规则
1、accumulator累加器可以使用SparkContext.accumulator([初始值])创建
2、使用.add()进行累加
3、在task中,如foreach循环中,不能读取累加器的值
4、只有驱动程序,也就是循环外,才可以使用.value读取累加器的值
创建范例
i = sc.parallelize([3, 1, 2, 5, 5])
创建累加器t,初始值为0.0
t = sc.accumulator(0.0)
创建累加器n,初始值为0
n = sc.accumulator(0)
使用foreach传入参数i,针对每一项数据执行
t累加元素值,n累加元素个数
i.foreach(lambda x:[t.add(x), n.add(1)])
计算平均=求和/计数,并显示总和、数量
avg = t.value / n.value
avg
t.value
n.value
8、Persistence持久化
RDD持久化机制可以用于将需要重复运算的RDD存储在内存中,以便大幅提升运算效率
使用方法
1、RDD.persist(存储等级)——可以指定存储等级,默认是MEMORY_ONLY,也就是存储在内存中
2、RDD.unpersist()——取消持久化
MEMORY_ONLY
这是默认选项。存储RDD的方式是以Java对象反串行化在JVM内存中,如果RDD太大无法完全存储在内存中,多余的RDD partitions不会缓存在内存中,而是需要时再重新计算
MEMORY_AND_DISK
存储RDD的方式是以Java对象反串行化在JVM内存中,如果RDD太大无法完全存储在内存中,多余的RDD partitions存储在硬盘中,需要时,再从硬盘读取
MEMORY_ONLY_SER
存储RDD的以Java对象串行化。由于需要在进行反串行化才能使用,所以会多使用CPU计算资源,但比较省内存空间。多余的RDD partitions不会缓存在内存中,而是需要时再重新计算
MEMORY_AND_DISK_SER
与MEMORY_ONLY_SER类似,多余的RDD partitions存储到硬盘,需要时再读取
DISK_ONLY
存储RDD在硬盘上
MEMORY_ONLY_2,MEMORY_AND_DISK_2,etc
与上列一样,但每个RDD partitions都复制到两个节点
使用过程
使用持久化
i = sc.parallelize([3,1,2,5,5])
i.persist()
i.is_cached
取消持久化
i.unpersist()
i.is_cached
设置存储等级范例
from pyspark.storagelevel import StorageLevel
i.persist(StorageLevel.MEMORY_AND_DISK)
i.is_cached
9、Spark创建WordCount
创建文件夹
mkdir /usr/sparktest
创建txt文件
vim /usr/sparktest/test.txt
进入pyspark
pyspark --master local[*]
执行以下操作
text = sc.textFile("/usr/sparktest/test.txt")
s = text.flatMap(lambda line:line.split(" ") )
count = s.map(lambda word:(word,1)).reduceByKey(lambda x,y:x+y)
count.saveAsTextFile("/usr/sparktest/output")
查看输出目录