from pyspark import SparkContext, SparkConf
$ PYSPARK_PYTHON=python3.4 bin/pyspark
$ PYSPARK_PYTHON=/opt/pypy-2.5/bin/pypy bin/spark-submit examples/src/main/python/pi.py
conf = SparkConf().setAppName(appName).setMaster(master)
sc = SparkContext(conf=conf)
$ ./bin/pyspark --master local[4]
$ ./bin/pyspark --master local[4] --py-files code.py
$ PYSPARK_DRIVER_PYTHON=ipython ./bin/pyspark
$ PYSPARK_DRIVER_PYTHON=jupyter PYSPARK_DRIVER_PYTHON_OPTS=notebook ./bin/pyspark
data = [1, 2, 3, 4, 5]
distData = sc.parallelize(data)
distData.reduce(lambda a, b: a + b)
sc.parallelize(data, 10)
>>> distFile = sc.textFile("data.txt")
distFile.map(lambda s: len(s)).reduce(lambda a, b: a + b)
textFile("/my/directory"), textFile("/my/directory/*.txt"), ("/my/directory/*.gz")
Writable Type | Python Type |
---|---|
Text | unicode str |
IntWritable | int |
FloatWritable | float |
DoubleWritable | float |
BooleanWritable | bool |
BytesWritable | bytearray |
NullWritable | None |
MapWritable | dict |
array类型需自定义类型转换器,读取时,默认的转换器会将用户自定义的ArrayWritable转换为Java的Object[ ],序列化为python元组。
>>> rdd = sc.parallelize(range(1, 4)).map(lambda x: (x, "a" * x))
>>> rdd.saveAsSequenceFile("path/to/file")
>>> sorted(sc.sequenceFile("path/to/file").collect())
[(1, u'a'), (2, u'aa'), (3, u'aaa')]
$ ./bin/pyspark --jars /path/to/elasticsearch-hadoop.jar
>>> conf = {"es.resource" : "index/type"} # assume Elasticsearch is running on localhost defaults
>>> rdd = sc.newAPIHadoopRDD("org.elasticsearch.hadoop.mr.EsInputFormat",
"org.apache.hadoop.io.NullWritable",
"org.elasticsearch.hadoop.mr.LinkedMapWritable",
conf=conf)
>>> rdd.first() # the result is a MapWritable that is converted to a Python dict
(u'Elasticsearch ID',
{u'field1': True,
u'field2': u'Some Text',
u'field3': 12345})
如果使用自定义序列化数据,例如从HBase加载数据,需要先在Java或者Scala侧将数据转换为 Pyrolite’的pickler可以处理的东西。
统计文件中字符数量
lines = sc.textFile("data.txt")
lineLengths = lines.map(lambda s: len(s))
totalLength = lineLengths.reduce(lambda a, b: a + b)
lineLengths.persist()
函数式编程
"""MyScript.py"""
if __name__ == "__main__":
def myFunc(s):
words = s.split(" ")
return len(words)
sc = SparkContext(...)
sc.textFile("file.txt").map(myFunc)
面向对象编程
class MyClass(object):
def func(self, s):
return s
def doStuff(self, rdd):
return rdd.map(self.func)
如果将变量定义在了对象之外,如下所示,会将整个对象发送给集群
class MyClass(object):
def __init__(self):
self.field = "Hello"
def doStuff(self, rdd):
return rdd.map(lambda s: self.field + s)
应该将传入spark的数值定义为本地变量,则只会讲变量的值发送给集群
def doStuff(self, rdd):
field = self.field
return rdd.map(lambda s: field + s)
理解变量和方法被传入到集群的里程碑和生命周期
counter = 0
rdd = sc.parallelize(data)
# Wrong: Don't do this!!
def increment_counter(x):
global counter
counter += x
rdd.foreach(increment_counter)
print("Counter value: ", counter)
统计内同相同行的数量
lines = sc.textFile("data.txt")
pairs = lines.map(lambda s: (s, 1))
counts = pairs.reduceByKey(lambda a, b: a + b)
按字符顺序排序
lines = sc.textFile("data.txt")
pairs = lines.map(lambda s: (s, 1))
counts = pairs.sortByKey()
Transformations | 含义 |
---|---|
map(func) | 将源RDD的每个元素通过func进行计算,返回一个新的RDD |
filter(func) | 将源RDD中的每个元素传入func,将func计算返回true的元素作为新的RDD |
flatMap(func) | 和Map类似,但是每个输入元素map处理后,会有0个或者多个输出,func函数的返回值是一个序列 |
mapPartitions(func) | 类似于map,但是以RDD的分片(block)为单位进行处理的,例如,处理元素为T的RDD时,func的输入输出形式为 Iterator |
mapPartitionsWithIndex(func) | 和mapPartitions类似,但是可以将一个整型值作为源RDD分片的索引,例如处理元素为T的RDD时,func的输入输出形式为 (Int, Iterator |
sample(withReplacement, fraction, seed) | 使用给定的随机数发生器种子,以可选的替换值对数据的一小部分进行采样。 |
union(otherDataset) | 返回一个新的数据集,该数据集包含源RDD和参数中RDD元素的并集。 |
intersection(otherDataset) | 返回包含源RDD和参数中元素的交集的新RDD。 |
distinct([numPartitions])) | 返回一个包含源数据集的不同元素的新数据集。 |
groupByKey([numPartitions]) | 在(K , V)RDD上调用时,返回(K , Iterable< V >)对的数据集。注意:如果要在每个键上执行聚合(如求和或平均值),则使用reduceByKey或AggregateByKey将获得更好的性能。注意:默认情况下,输出的并行度取决于父RDD的分区数量。可以通过一个可选的numPartitions参数来设置不同数量的任务。 |
reduceByKey(func, [numPartitions]) | 在(K,V)RDD对上调用时,返回(K,V)对的数据集,使用给定的减少函数func聚合每个密钥的值,该函数必须是类型(V,V)= > V,通过可选的第二个参数可配置减少任务的个数。 |
aggregateByKey(zeroValue)(seqOp, combOp, [numPartitions]) | 举例 |
sortByKey([ascending], [numPartitions]) | 在一个Key可排序的的(K,V)RDD上调用时,根据传入的ascending参数(true为升序,false为降序),按K对源RDD中的参数进行排序,然后新的RDD |
join(otherDataset, [numPartitions]) | 在(K,V)和(K,W)RDD上调用时,对每个key做聚合,返回(K,(V,W))的RDD,还可以执行eftOuterJoin, rightOuterJoin, and fullOuterJoin三种join |
cogroup(otherDataset, [numPartitions]) | 在(K,V)和(K,W)RDD上调用时,返回一个元素为(K, (Iterable |
cartesian(otherDataset) | 在类型为T和U的RDD上调用时,返回元素类型为(T,U)元组的RDD |
pipe(command, [envVars]) | 通过系统的stdin和和stdout源RDD和结果RDD输入和从目的RDD输出(以字符串的形式)可以传入bash或者perl命令 |
coalesce(numPartitions) | 将RDD的分区减少到制定数目,通常在使用filter将一个大的数据集精简之后使用 |
repartition(numPartitions) | 按指定的数量分区重新分区,可以增加也可以减少,通常所有数据都需要重新shuffle,占用较多带宽 |
repartitionAndSortWithinPartitions(partitioner) | 重新分区后,再在各分区内对各元素按键排序,由于在shuffle的时候就已经排序了,比repartition效率更高 |
Action | 意义 |
---|---|
reduce(func) | 使用func将源RDD中的元素聚合 |
collect() | 将RDD的所有元素都返回给驱动程序,通常用在filter或者别的返回比较小的子数据集的场景 |
count() | 返回RDD元素的个数 |
first() | 返回RDD的第一个元素 |
take(n) | 返回RDD的前n个元素 |
takeSample(withReplacement, num, [seed]) | 按随机采样的方式返回n个元素,可以设置替换值和随机数种子 |
saveAsTextFile(path) | 将RDD保存为文件,存在本地或者其他Hadoop支持的文件系统中 |
saveAsSequenceFile(path) (Java and Scala) | 将数据集的元素作为Hadoop SequenceFile写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统中的给定路径中。 这可以在实现Hadoop的Writable接口的键值对的RDD上使用。 在Scala中,它也可以在可隐式转换为Writable的类型上使用(Spark包括基本类型的转换,如Int,Double,String等)。 |
saveAsObjectFile(path) (Java and Scala) | 使用Java 序列化进行格式化,可以使用SparkContext.objectFile()来加载 |
countByKey() | 在类型为(K,V)的RDD上调用,返回一个元素类型为(K,int)的hashmap,计算每个K的个数 |
foreach(func) | 在数据集的每个元素上运行函数func。 不适用于如更新累加器或与外部存储系统交互的场景。 |
RDD API中也支持部分异步场景,例如foreachAsync,不会造成进程堵塞
Storage Level | 含义 |
---|---|
MEMORY_ONLY | 将RDD以反序列化Java对象存储在JVM中。如果RDD不适合放在内存中,一些分区将不会被缓存,每次需要用的时候会被重新计算,为默认级别 |
MEMORY_AND_DISK | 将RDD以反序列化Java对象存储在JVM中,如果RDD不适合放在内存中,将这些RDD的分区放在磁盘中,当需要被用到的时候会被读取 |
MEMORY_ONLY_SER (Java and Scala) | 将RDD以序列化Java对象的方式存储在内存中,每个分区一个byte数组。比非序列化的存储方式空间利用率更高,但是会消耗更多CPU |
MEMORY_AND_DISK_SER (Java and Scala) | 和MEMORY_ONLY_SER类似,但是会将不适合放在内存中的分区存储在硬盘 |
DISK_ONLY | 将RDD分区只存储在硬盘上 |
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc | 和上面的级别一致,但是会缓存在集群中的两个节点 |
OFF_HEAP (experimental) | 类似MEMORY_ONLY_SER,但是是将数据存储在堆外内存 |
python会始终使用pickle库对RDD的分区进行序列化,所以不存选择是否序列化,只有如下级别MEMORY_ONLY, MEMORY_ONLY_2, MEMORY_AND_DISK, MEMORY_AND_DISK_2, DISK_ONLY, and DISK_ONLY_2
spark也会自动persist某些中间数据,用于避免节点shuffle失败后的重复计算,但是如果数据确实可能复用,仍然建议手工申明presist。
spark基于最近至少使用过的原则自动删除缓存数据,如果想要手动清除,使用RDD.unpersist()方法。