说明:本翻译基本遵照Spark的官方翻译,但是某些不太顺口的地方还是做了一下处理,请原谅我poor的外语能力。官网链接:http://spark.apache.org/docs/latest/quick-start.html
使用Spark Shell进行交互分析
基本操作
Spark的shell提供了一种简单的方式来学习Api,同时它也是一个强大的具有交互功能的数据分析工具。无论是Python还是Scala(Scala运行在Java虚拟机上,同时能够非常方便的使用Java类库),它都是可用的。在Spark的安装目录中使用如下命令来启动Spark Shell:
Scala
.bin/spark-shell
Python
.bin/pyspark
Spark中最主要的抽象概念是一个分布式的记录集合,即弹性分布式数据集(RDD)。RDD可以由Hadoop的输入来创建(比如HDFS上的文件)或者由其它RDD转换而来。下面让我们使用Spark源目录中的README文件内容创建一个RDD。
>>>textFile = sc.textFile("README.md")
RDD可以使用动作(action)来返回值,或者使用转换(transformations)操作变成新的RDD。下面是一些动作的示例:
>>>textFile.count() #这个RDD中记录的条目
126
>>>textFile.first() #这个RDD的第一条记录
u'# Apache Spark'
现在让我们使用一个转换操作。我们将使用filter转换操作来得到一个新的RDD,这个RDD是原来文件记录的一个子集(过滤)
>>>linesWithSpark = textFile.filter(lambda line: "Spark" in line)
我们可以将动作和转换操作组合在一起成为一个链
>>>textFile.filter(lambda line: "Spark" in line).count() #有多少行含有"Spark"?
15
更多关于RDD的操作
RDD的动作和转换可以用于做复杂的计算。让我们实现一个任务,输出是含有最多单词的那一行含有多少个单词:
Scala
scala> textFile.map(line => line.split(" ").size).reduce((a, b) => if(a > b) a else b)
res4: Long=15
Python
>>>textFile.map(lambda line : len(line.split(" "))).reduce(lambda a, b : a if(a > b) else b)
15
上面首先将每一行映射到了一个整数值,即创建了一个新的RDD。然后在新的RDD上调用了reduce动作来寻找最大的行单词数。map与reduce的入参形式是Python中的匿名函数(lambdas),但是我们也可以使用Python的top-level函数(这个不好解释,类似于函数中嵌套函数,top-level就是外面那个?)。例如,我们可以定义max函数来使代码更加便于理解。
>>>def max(a, b):
... if a > b:
... return a
... else:
... return b
>>>textFile.map(lambda line: len(line.split())).reduce(max)
15
MapReduce是一个典型的数据流格式,Hadoop的发展使得这种处理方式变得非常流行。Spark可以很方便的实现MapReduce:
>>>wordCounts = textFile.flatMap(lambda line: line.split()).map(lambda word: (word, 1)).reduceByKey(lambda a, b: a + b)
这里,我们将flatMap,Map和reduceByKey转换操作(reduceByKey我记得是action啊,这里官网使用了transformations)组合在一起来计算文件中每一个单词的数量,并返回一个RDD,形式是(String,int)型的键值对。
为了在shell中计算单词的数量,我们可以使用collect动作:
>>>wordCounts.collect()
[(u'and', 9), (u'A', 1), (u'webpage', 1), (u'README', 1), (u'Note', 1), (u'"local"', 1), (u'variable', 1), ...]
缓存
Spark也支持将数据集合缓存在整个集群的内存中。这对于数据的重用非常方便,比如当我们检索一个小型的”hot”数据集(其实就是使用频率比较高的)或者运行一些迭代算法比如PageRank.作为一个简单的例子,让我们把上面的linesWithSpark数据集缓存起来:
Scala
scala> linesWithSpark.cache()
res7: spark.RDD[String] = spark.FilteredRDD@17e51082
scala> linesWithSpark.count()
res8: Long = 19
scala> linesWithSpark.count()
res9: Long = 19
使用Spark来对一个只有100行的文本文件进行探索和缓存看起来似乎有点”愚蠢”。但是这些方法也可以运行在超大数据集合上,即使这些数据横跨了数十甚至数百个节点,这个可能比较有趣。你依然可以将bin/spark-shell连接到集群上进行这些交互式的操作,具体可以参考programming guide(这个后续翻译)。
独立应用
假设我们希望使用Spark的API来编写独立的应用程序。我们将分别使用Scala(sbt),Java(Maven)以及python通过一个简单的应用来做一个示范。
Scala
/* SimpleApp.scala*/
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
object SimpleApp{
def main(args: String[]){
val logFile = "YOUR_SPARK_HOME/README.md"
val conf = new SparkConf().setAppName("Simple Application")
val sc = new SparkContext(conf)
val logData = sc.textFile(logFile, 2).cache()
val numAs = logData.filter(line => line.contains("a")).count()
val numBs = logData.filter(line => line.contains("b")).count()
println("Line with a: %s,Line with b: %s".format(numAs, numBs))
}
}
Java
/* SimpleApp.java */
import org.apache.spark.api.java.*;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.Function;
public class SimpleApp {
public static void main(String[] args) {
String logFile = "YOUR_SPARK_HOME/README.md"; // Should be some file on your system
SparkConf conf = new SparkConf().setAppName("Simple Application");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> logData = sc.textFile(logFile).cache();
long numAs = logData.filter(new Function<String, Boolean>() {
public Boolean call(String s) { return s.contains("a"); }
}).count();
long numBs = logData.filter(new Function<String, Boolean>() {
public Boolean call(String s) { return s.contains("b"); }
}).count();
System.out.println("Lines with a: " + numAs + ", lines with b: " + numBs);
}
}
Python
"""SimpleApp.py"""
from pyspark improt SparkContext
logFile = "YOUR_SPARK_HOME/README.md" # Should be some file on your system
sc = SparkContext("local", "Simple App")
logData = sc.textFile(logFile).cache()
numAs = logData.filter(lambda s: 'a' in s).count()
numBs = logData.filter(lambda s: 'b' in s).count()
print("Lines with a: %i, lines with b: %i" % (numAs, numBs))
这个项目只是统计了一个文本文件中包含了字符‘a’的行数以及包含字符’b’的行数。记着你需要将
YOUR_SPARK_HOME替换成Spark的安装目录。对于使用Scala和Java的例子,我们使用了SparkContext来创建RDD。我们可以将Python函数转到Spark上来,它们所引用的任何变量都会被自动序列化。对于使用了自定义类或者第三方库的应用,我们也可以将它们打包成.zip文件,然后通过spark-submit的–py-files参数添加进来(使用spark-submit –help获取详情)。SimpleApp太简单了了,不需要依赖任何其它代码。
我们可以通过bin/spark-submit脚本来运行上面的应用:
# Use spark-submit to run your application
$ YOUR_SPARK_HOME/bin/spark-submit \
--master local[4] \
SimpleApp.py
...
Lines with a: 46, Lines with b: 23