Spark-3.5.7文档1 - 快速开始

本教程将快速介绍如何使用Spark。我们将首先通过Spark的交互式shell(支持PythonScala)介绍API,然后演示如何用JavaScalaPython编写应用程序。

要跟随本指南学习,请先从Spark官网下载打包发布的Spark版本。由于我们不使用HDFS,您可以下载适用于任何Hadoop版本的安装包。

请注意,在Spark 2.0之前,Spark的主要编程接口是弹性分布式数据集(RDD)。Spark 2.0之后,RDD被数据集(Dataset)取代,DatasetRDD一样是强类型的,但在底层进行了更丰富的优化。RDD接口仍然受支持,您可以在RDD编程指南中查看更详细的参考。但我们强烈建议您转向使用Dataset,其性能优于RDD。有关Dataset的更多信息,请参阅SQL编程指南。

使用 Spark Shell 进行交互式分析

基本操作

Spark Shell提供了一种简单的方式来学习API,同时也是进行交互式数据分析的强大工具。它支持Scala(运行在Java虚拟机上,因此可以很好地使用现有的Java库)和Python两种语言。在Spark目录中运行以下命令来启动Spark Shell

Python 方式
shell 复制代码
./bin/pyspark

或者如果当前环境中已通过 pip 安装了 PySpark:

shell 复制代码
pyspark

Spark 的主要抽象概念是一个称为数据集(Dataset)的分布式项目集合。数据集可以从 Hadoop InputFormats(如 HDFS 文件)创建,也可以通过转换其他数据集来生成。

由于 Python 的动态特性,在 Python 中我们不需要数据集是强类型的。因此,Python 中的所有数据集都是 Dataset[Row] 类型,我们将其称为 DataFrame,以与 Pandas 和 R 中的数据框概念保持一致。

让我们从 Spark 源码目录中的 README 文件文本创建一个新的 DataFrame:

python 复制代码
>>> textFile = spark.read.text("README.md")

您可以直接通过对 DataFrame 调用某些操作(actions)来获取值,或者转换(transform)DataFrame 以生成新的 DataFrame。有关更多详细信息,请阅读 API 文档。

python 复制代码
>>> textFile.count()  # Number of rows in this DataFrame
126

>>> textFile.first()  # First row in this DataFrame
Row(value=u'# Apache Spark')

现在让我们将这个 DataFrame 转换为一个新的 DataFrame。我们调用 filter 方法来返回一个包含文件中部分行的子集的新 DataFrame。

python 复制代码
>>> linesWithSpark = textFile.filter(textFile.value.contains("Spark"))

当然可以!我们可以将转换(transformations)和操作(actions)串联在一起使用。例如:

python 复制代码
>>> textFile.filter(textFile.value.contains("Spark")).count()  # How many lines contain "Spark"?
15
Scala 方式
scala 复制代码
./bin/spark-shell

Spark 的主要抽象概念是一个称为数据集(Dataset)的分布式项目集合。数据集可以从 Hadoop InputFormats(如 HDFS 文件)创建,也可以通过转换其他数据集来生成。让我们从 Spark 源码目录中的 README 文件文本创建一个新的数据集:

scala 复制代码
scala> val textFile = spark.read.textFile("README.md")
textFile: org.apache.spark.sql.Dataset[String] = [value: string]

您可以直接通过对数据集(Dataset)调用某些操作(actions)来获取值,或者转换(transform)数据集以生成新的数据集。有关更多详细信息,请阅读 API 文档。

scala 复制代码
scala> textFile.count() // Number of items in this Dataset
res0: Long = 126 // May be different from yours as README.md will change over time, similar to other outputs

scala> textFile.first() // First item in this Dataset
res1: String = # Apache Spark

现在让我们将这个数据集(Dataset)转换为一个新的数据集。我们调用 filter 方法来返回一个包含文件中部分项目的子集的新数据集。

scala 复制代码
scala> val linesWithSpark = textFile.filter(line => line.contains("Spark"))
linesWithSpark: org.apache.spark.sql.Dataset[String] = [value: string]

我们可以将转换(transformations)和操作(actions)串联在一起使用:

scala 复制代码
scala> textFile.filter(line => line.contains("Spark")).count() // How many lines contain "Spark"?
res3: Long = 15

关于数据集操作的更多内容

数据集操作和转换可用于更复杂的计算。假设我们想要找到单词数量最多的那一行:

Python 方式
python 复制代码
>>> from pyspark.sql import functions as sf
>>> textFile.select(sf.size(sf.split(textFile.value, "\s+")).name("numWords")).agg(sf.max(sf.col("numWords"))).collect()
[Row(max(numWords)=15)]

首先,它将一行数据映射为一个整数值,并将其别名为 "numWords",从而创建一个新的 DataFrame。随后在该 DataFrame 上调用 agg 方法以查找最大的单词计数。select 和 agg 的参数均为 Column 类型,我们可以通过 df.colName 从 DataFrame 中获取列。此外,我们还可以导入 pyspark.sql.functions 模块,该模块提供了许多便捷函数来基于现有列构建新列。

一个常见的数据流模式是 MapReduce,正如 Hadoop 所推广的那样。Spark 可以轻松实现 MapReduce 流程:

python 复制代码
>>> wordCounts = textFile.select(sf.explode(sf.split(textFile.value, "\s+")).alias("word")).groupBy("word").count()

在此,我们使用 select 中的 explode 函数,将行数据集转换为单词数据集,然后结合 groupBy 和 count 方法,计算出文件中每个单词的计数,生成一个包含两列("word" 和 "count")的 DataFrame。为了在交互式环境中收集单词计数结果,我们可以调用 collect 方法:

python 复制代码
>>> wordCounts.collect()
[Row(word=u'online', count=1), Row(word=u'graphs', count=1), ...]
Scala 方式
scala 复制代码
scala> textFile.map(line => line.split(" ").size).reduce((a, b) => if (a > b) a else b)
res4: Int = 15

首先,它将一行数据映射为一个整数值,从而创建一个新的数据集。随后在该数据集上调用 reduce 方法以查找最大的单词计数。map 和 reduce 的参数均为 Scala 函数字面量(闭包),可以使用任何语言特性或 Scala/Java 库。例如,我们可以轻松调用在其他地方声明的函数。这里我们将使用 Math.max() 函数来使代码更易理解:

scala 复制代码
scala> import java.lang.Math
import java.lang.Math

scala> textFile.map(line => line.split(" ").size).reduce((a, b) => Math.max(a, b))
res5: Int = 15

一个常见的数据流模式是 MapReduce,正如 Hadoop 所推广的那样。Spark 可以轻松实现 MapReduce 流程:

scala 复制代码
scala> val wordCounts = textFile.flatMap(line => line.split(" ")).groupByKey(identity).count()
wordCounts: org.apache.spark.sql.Dataset[(String, Long)] = [value: string, count(1): bigint]

在这里,我们调用 flatMap 将行数据集转换为单词数据集,然后结合 groupByKey 和 count 方法,计算出文件中每个单词的计数,生成一个包含 (String, Long) 键值对的数据集。为了在交互式环境中收集单词计数结果,我们可以调用 collect 方法:

scala 复制代码
scala> wordCounts.collect()
res6: Array[(String, Int)] = Array((means,1), (under,2), (this,3), (Because,1), (Python,2), (agree,1), (cluster.,1), ...)

缓存

Spark 还支持将数据集拉取到集群范围内的内存缓存中。这在需要重复访问数据时非常有用,例如查询小型"热"数据集或运行像 PageRank 这样的迭代算法时。举个简单的例子,我们将标记 linesWithSpark 数据集进行缓存:

Python 方式
python 复制代码
>>> linesWithSpark.cache()

>>> linesWithSpark.count()
15

>>> linesWithSpark.count()
15

使用 Spark 来探索和缓存一个仅 100 行的文本文件可能看起来有些小题大做。但有趣之处在于,这些相同的函数可以应用于非常庞大的数据集,即使这些数据分布在数十个甚至数百个节点上。您还可以通过将 bin/pyspark 连接到集群来交互式地进行此类操作,正如 RDD 编程指南中所描述的那样。

Scala 方式
scala 复制代码
scala> linesWithSpark.cache()
res7: linesWithSpark.type = [value: string]

scala> linesWithSpark.count()
res8: Long = 15

scala> linesWithSpark.count()
res9: Long = 15

使用 Spark 来探索和缓存一个仅 100 行的文本文件可能看起来有些小题大做。但有趣之处在于,这些相同的函数可以应用于非常庞大的数据集,即使这些数据分布在数十个甚至数百个节点上。您还可以通过将 bin/spark-shell 连接到集群来交互式地进行此类操作,正如 RDD 编程指南中所描述的那样。

独立应用程序

假设我们希望使用 Spark API 编写一个独立应用程序。我们将通过 Scala(使用 sbt)、Java(使用 Maven)和 Python(使用 pip)分别演示一个简单的应用示例。

Python 方式

现在我们将展示如何使用 Python API(PySpark)编写应用程序。

如果您正在构建打包的 PySpark 应用程序或库,可以将其添加到 setup.py 文件中,具体如下:

python 复制代码
    install_requires=[
        'pyspark==3.5.7'
    ]

例如,我们将创建一个简单的 Spark 应用程序:SimpleApp.py

python 复制代码
"""SimpleApp.py"""
from pyspark.sql import SparkSession

logFile = "YOUR_SPARK_HOME/README.md"  # Should be some file on your system
spark = SparkSession.builder.appName("SimpleApp").getOrCreate()
logData = spark.read.text(logFile).cache()

numAs = logData.filter(logData.value.contains('a')).count()
numBs = logData.filter(logData.value.contains('b')).count()

print("Lines with a: %i, lines with b: %i" % (numAs, numBs))

spark.stop()

该程序仅统计文本文件中包含字母 "a" 的行数和包含字母 "b" 的行数。请注意,您需要将 YOUR_SPARK_HOME 替换为 Spark 的实际安装路径。与 Scala 和 Java 示例类似,这里我们使用 SparkSession 来创建数据集。对于需要使用自定义类或第三方库的应用程序,我们还可以通过 --py-files 参数将代码依赖项打包成 .zip 文件后提交给 spark-submit(具体细节可参考 spark-submit --help 命令)。由于 SimpleApp 非常简单,我们无需指定任何代码依赖项。

我们可以使用 bin/spark-submit 脚本来运行这个应用程序:

python 复制代码
# 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

如果您已在环境中通过 pip 安装了 PySpark(例如使用 pip install pyspark),则可以选择使用常规的 Python 解释器运行应用程序,或根据需要直接使用提供的 "spark-submit" 命令。

python 复制代码
# Use the Python interpreter to run your application
$ python SimpleApp.py
...
Lines with a: 46, Lines with b: 23

其他依赖管理工具(如 Conda 和 pip)也可用于管理自定义类或第三方库。另请参阅 Python 包管理 相关文档。

Scala 方式

我们将用 Scala 创建一个非常简单的 Spark 应用程序------事实上它简单到直接命名为 SimpleApp.scala:

scala 复制代码
/* SimpleApp.scala */
import org.apache.spark.sql.SparkSession

object SimpleApp {
  def main(args: Array[String]): Unit = {
    val logFile = "YOUR_SPARK_HOME/README.md" // Should be some file on your system
    val spark = SparkSession.builder.appName("Simple Application").getOrCreate()
    val logData = spark.read.textFile(logFile).cache()
    val numAs = logData.filter(line => line.contains("a")).count()
    val numBs = logData.filter(line => line.contains("b")).count()
    println(s"Lines with a: $numAs, Lines with b: $numBs")
    spark.stop()
  }
}

请注意,应用程序应定义一个 main() 方法,而不是继承 scala.App 类。继承 scala.App 的子类可能无法正常工作。

该程序仅统计 Spark README 文件中包含字母 'a' 的行数和包含字母 'b' 的行数。请注意,您需要将 YOUR_SPARK_HOME 替换为 Spark 的实际安装路径。与之前使用 Spark shell 的示例(它会自行初始化 SparkSession)不同,这里我们将 SparkSession 的初始化作为程序的一部分。

我们通过调用 SparkSession.builder 来构建 SparkSession,然后设置应用程序名称,最后调用 getOrCreate 来获取 SparkSession 实例。

由于我们的应用程序依赖于 Spark API,因此还需要包含一个 sbt 配置文件 build.sbt,其中需声明 Spark 为依赖项。该文件还需添加 Spark 所依赖的代码库:

scala 复制代码
name := "Simple Project"

version := "1.0"

scalaVersion := "2.12.18"

libraryDependencies += "org.apache.spark" %% "spark-sql" % "3.5.7"

为了让 sbt 能够正确工作,我们需要按照典型的目录结构来布局 SimpleApp.scala 和 build.sbt 文件。完成目录结构配置后,我们就可以创建一个包含应用程序代码的 JAR 包,然后使用 spark-submit 脚本来运行程序。

scala 复制代码
# Your directory layout should look like this
$ find .
.
./build.sbt
./src
./src/main
./src/main/scala
./src/main/scala/SimpleApp.scala

# Package a jar containing your application
$ sbt package
...
[info] Packaging {..}/{..}/target/scala-2.12/simple-project_2.12-1.0.jar

# Use spark-submit to run your application
$ YOUR_SPARK_HOME/bin/spark-submit \
  --class "SimpleApp" \
  --master local[4] \
  target/scala-2.12/simple-project_2.12-1.0.jar
...
Lines with a: 46, Lines with b: 23

其他依赖管理工具(如 Conda 和 pip)也可用于管理自定义类或第三方库。另请参阅 Python 包管理 相关文档。

Java 方式

此示例将使用 Maven 编译应用程序 JAR 包,但任何类似的构建系统都可以使用。

我们将创建一个非常简单的 Spark 应用程序 SimpleApp.java:

java 复制代码
/* SimpleApp.java */
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.Dataset;

public class SimpleApp {
  public static void main(String[] args) {
    String logFile = "YOUR_SPARK_HOME/README.md"; // Should be some file on your system
    SparkSession spark = SparkSession.builder().appName("Simple Application").getOrCreate();
    Dataset<String> logData = spark.read().textFile(logFile).cache();

    long numAs = logData.filter(s -> s.contains("a")).count();
    long numBs = logData.filter(s -> s.contains("b")).count();

    System.out.println("Lines with a: " + numAs + ", lines with b: " + numBs);

    spark.stop();
  }
}

该程序仅统计 Spark README 文件中包含字母 'a' 的行数和包含字母 'b' 的行数。请注意,您需要将 YOUR_SPARK_HOME 替换为 Spark 的实际安装路径。与之前使用 Spark shell 的示例(它会自行初始化 SparkSession)不同,这里我们将 SparkSession 的初始化作为程序的一部分。

为了构建程序,我们还需要编写一个 Maven pom.xml 文件,其中需声明 Spark 为依赖项。请注意,Spark 构件都带有 Scala 版本标签。

xml 复制代码
<project>
  <groupId>edu.berkeley</groupId>
  <artifactId>simple-project</artifactId>
  <modelVersion>4.0.0</modelVersion>
  <name>Simple Project</name>
  <packaging>jar</packaging>
  <version>1.0</version>
  <dependencies>
    <dependency> <!-- Spark dependency -->
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-sql_2.12</artifactId>
      <version>3.5.7</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

我们按照标准的 Maven 目录结构来布局这些文件:

shell 复制代码
$ find .
./pom.xml
./src
./src/main
./src/main/java
./src/main/java/SimpleApp.java

现在,我们可以使用 Maven 打包应用程序,并通过 ./bin/spark-submit 来执行它。

shell 复制代码
# Package a JAR containing your application
$ mvn package
...
[INFO] Building jar: {..}/{..}/target/simple-project-1.0.jar

# Use spark-submit to run your application
$ YOUR_SPARK_HOME/bin/spark-submit \
  --class "SimpleApp" \
  --master local[4] \
  target/simple-project-1.0.jar
...
Lines with a: 46, Lines with b: 23

其他依赖管理工具(如 Conda 和 pip)也可用于管理自定义类或第三方库。另请参阅 Python 包管理相关文档。

下一步学习方向

恭喜您成功运行第一个 Spark 应用程序!

  • 若需深入了解 API,请从《RDD 编程指南》和《SQL 编程指南》开始,或查看"编程指南"菜单中的其他组件文档。

  • 如需在集群上运行应用程序,请参阅《部署指南》。

  • 此外,Spark 在 examples 目录中提供了多个示例(Scala、Java、Python、R),您可以通过以下方式运行它们:

shell 复制代码
# For Scala and Java, use run-example:
./bin/run-example SparkPi

# For Python examples, use spark-submit directly:
./bin/spark-submit examples/src/main/python/pi.py

# For R examples, use spark-submit directly:
./bin/spark-submit examples/src/main/r/dataframe.R
相关推荐
qqxhb1 天前
系统架构设计师备考第68天——大数据处理架构
大数据·hadoop·flink·spark·系统架构·lambda·kappa
xiaoshu_yilian1 天前
pyspark入门实操(收藏版)
spark
梦里不知身是客112 天前
Spark的容错机制
大数据·分布式·spark
乌恩大侠2 天前
【Spark】操作记录
人工智能·spark·usrp
大数据CLUB2 天前
酒店预订数据分析及预测可视化
大数据·hadoop·分布式·数据挖掘·数据分析·spark·mapreduce
新知图书3 天前
RDD的特点、算子与创建方法
数据分析·spark·1024程序员节
青云交3 天前
Java 大视界 -- 基于 Java 的大数据可视化在城市空气质量监测与污染溯源中的应用
java·spark·lstm·可视化·java 大数据·空气质量监测·污染溯源
Lansonli4 天前
大数据Spark(七十二):Transformation转换算子repartition和coalesce使用案例
大数据·分布式·spark
lucky_syq4 天前
Scala与Spark算子:大数据处理的黄金搭档
开发语言·spark·scala