python 使用spark加速 spark python入门教程_spark入门


1 文章说明

需要用到spark,特地写一个文章作为入门总结。

环境介绍:

  • 系统:centos7
  • python:python2.7.5
  • java:java1.8.0
  • hadoop:hadoop2.7
  • spark:spark3.0
  • 参考文档:http://spark.apache.org/docs/latest/quick-start.html

2 spark简介

简单地说,spark扩展了MapReduce计算模型,数据在内存中并行式计算。

3 安装spark

①验证java是否安装:java -version,已安装为java1.8.0。

②验证Scala是否安装:scala -version。

如果未安装scala,scala的安装步骤:

  • 1)下载scala,下载网址:https://www.scala-lang.org/download/,本次选择了scala-2.13.1.tgz文件。
  • 2)执行命令tar -zxvf scala-2.13.1.tgz。
  • 3)设置环境变量:切换到root账户,在/etc/profile文件中配置export SCALA_HOME=/home/grid/scala和export PATH=$PATH:$SCALA_HOME/bin,然后source /etc/profile,gird账户也需要source /etc/profile。
  • 4)scala -version验证是否安装成功。

③下载和安装spark:

  • 1)本次下载的是spark-3.0.0-preview-bin-hadoop2.7.tgz。
  • 2)解压文件,tar -zxvf spark-3.0.0-preview-bin-hadoop2.7.tgz。
  • 3)输入spark-shell进入到spark,python使用pyspark进入。

4 RDD弹性分布式数据集

4.1 RDD基本概念

RDD,resilient distributed dataset,弹性分布式数据集。spark的RDD是不可变的、分布式的数据集合。

  1. RDD会被划分为多个分区,运行在集群的不同节点。
  2. RDD的数据类型可以是java、scala、python的数据类型,也可以是用户自定义的。
  3. 定义RDD时,spark会惰性计算这些值。只有spark开始转化操作时,了解到完整的数据转化链,才会去计算,计算真正需求的数据。
  4. 每当我们调用一个新的行动操作时,整个 RDD 都会从头开始计算。要避免这种低效的行为,用户可以将中间结果持久化。
  5. 如果不重用 RDD,就没有必要浪费存储空间,再加上“在定义RDD时spark惰性计算的”,也就是说数据并没有加载,需要时直接重新计算,这就是为什么称之为“弹性”。

4.2 创建RDD

两种方式:

读取外部数据集:textFile函数可以加载一个外部数据集。

使用已存在的数据集:把已有集合传递给SparkContext.parallelize函数。

创建RDD示例:(使用Python语言)


>>> lines = sc.textFile("file:///home/grid/spark3/README.md")
>>> lines.take(5)
[u'# Apache Spark', u'', u'Spark is a unified...']
>>> 
>>> dozen = sc.parallelize(range(1,13))
>>> dozen.take(12)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]


4.3 RDD的两种操作

RDD的两种操作是:

转化操作:返回一个新的RDD的操作。

行动操作:向程序返回结果或把结果写入外部系统的操作,会触发实际的计算。

转化操作和行动操作的示例:使用filter函数过滤“python”、“java”相关的行,使用union函数合并这些行,使用take函数触发计算。


>>> lines = sc.textFile("file:///home/grid/spark3/README.md")
>>> java_lines = lines.filter(lambda x: 'Java' in x)
>>> java_lines.first()
u'high-level APIs in Scala, Java, Python, and R, and an optimized engine that'
>>> union_lines = python_lines.union(java_lines) 
>>> union_lines.take(10)
[u'high-level APIs ', u'## Interactive Python Shell',...]


注:省略部分输出结果。

4.4 向spark传递函数

可以传递lambda表达式、局部函数。

注意:传递类的函数时,不要直接写http://self.XXX,要复制给一个变量,因为spark会把整个对象发到工作节点。

传递函数示例


>>> def filter_scala_lines(s):
...     return 'Scala' in s
... 
>>> scala_lines = lines.filter(filter_scala_lines)
>>> scala_lines.collect()
[u'high-level APIs in Scala, ', u'## Interactive Scala Shell',...]


4.5 常见的转化操作和行动操作

常见的RDD转化操作:

  • map():将函数应用于 RDD 中的每个元素,将返回值构成新的 RDD
  • flatMap():将函数应用于 RDD 中的每个元素,将返回的迭代器的所有内容构成新的 RDD。通常用来切分单词
  • filter():返回一个由通过传给 filter()的函数的元素组成的 RDD
  • distinct():去重
  • sample(withReplacement, fraction, [seed]):对 RDD 采样,以及是否替换
  • union():生成一个包含两个 RDD 中所有元素的 RDD
  • intersection():求两个 RDD 共同的元素的 RDD
  • subtract():移除一个 RDD 中的内容(例如移除训练数据)
  • cartesian():与另一个 RDD 的笛卡儿积

常见的RDD行动操作:

  • collect() 返回 RDD 中的所有元素
  • count() RDD 中的元素个数
  • countByValue() 各元素在 RDD 中出现的次数
  • take(num) 从 RDD 中返回 num 个元素
  • top(num) 从 RDD 中返回最前面的 num个元素
  • takeOrdered(num)(ordering)从 RDD 中按照提供的顺序返回最前面的 num 个元素
  • takeSample(withReplacement, num, [seed])从 RDD 中返回任意一些元素
  • reduce(func) 并 行 整 合 RDD 中 所 有 数 据(例如 sum)
  • fold(zero)(func) 和 reduce() 一 样, 但 是 需 要提供初始值
  • aggregate(zeroValue)(seqOp, combOp)和 reduce() 相 似, 但 是 通 常返回不同类型的函数
  • foreach(func) 对 RDD 中的每个元素使用给定的函数

4.6 持久化

对 RDD 执行行动操作,每次都会重新计算RDD,这个时候可以使用persist函数对数据进行持久化。

出于不同的目的,我们可以为 RDD 选择不同的持久化级别:MEMORY_ONLY、DISK_ONLY等。

持久化示例


>>> python_lines
PythonRDD[9] at RDD at PythonRDD.scala:53
>>> python_lines.count()  # 第一次计算
3
>>> python_lines.max()  # 实际上进行了两次计算
u'high-level APIs in Scala...'
>>> python_lines.persist()  # 持久化
PythonRDD[9] at RDD at PythonRDD.scala:53
>>> from pyspark import StorageLevel
>>> java_lines.persist(StorageLevel.DISK_ONLY)  # 设置持久化级别
PythonRDD[11] at RDD at PythonRDD.scala:53


5 DataFrame

DataFrame是基于RDD,并且支持SparkSQL。

5.1 创建DataFrame

创建DataFrame的方式:已存在的RDD数据集、hive的表、其他数据源。

示例·使用已存在的RDD数据集创建DataFrame


>>> employee = spark.read.json('file:///home/grid/dataset/employee2.json')
>>> employee.show()
+---+---+--------+
|age| id|    name|
+---+---+--------+
| 25|  1|zhangsan|
| 28|  2|    lisi|
| 39|  3|  wangwu|
| 23|  4| zhaoliu|
| 23|  5|   sunqi|
+---+---+--------+


5.2 DataFrame的常见操作

  • printSchema:打印数据集的结构
  • show:打印数据集的数据
  • collect:返回所有的记录
  • select:选择某几列(返回值是DataFrame)
  • XX.age:属性选择的方式选择一列(返回值是Column)
  • XX['XX']:索引选择的方式选择一列
  • filter:按给定的条件筛选部分记录

DataFrame的常见操作示例


>>> employee.printSchema()
root
 |-- age: string (nullable = true)
 |-- id: string (nullable = true)
 |-- name: string (nullable = true)
>>> employee.show()
+---+---+--------+
|age| id|    name|
+---+---+--------+
| 25|  1|zhangsan|
| 28|  2|    lisi|
| 39|  3|  wangwu|
| 23|  4| zhaoliu|
| 23|  5|   sunqi|
+---+---+--------+
 
>>> employee.select('id', 'name')
DataFrame[id: string, name: string]
>>> employee.select('id', 'name').show()
+---+--------+
| id|    name|
+---+--------+
|  1|zhangsan|
|  2|    lisi|
|  3|  wangwu|
|  4| zhaoliu|
|  5|   sunqi|
+---+--------+
>>> employee.id
Column<id>
>>> employee['id']
Column<id>
>>> employee.filter('id<3')
DataFrame[age: string, id: string, name: string]
>>> employee.filter('id<3').show()
+---+---+--------+
|age| id|    name|
+---+---+--------+
| 25|  1|zhangsan|
| 28|  2|    lisi|
+---+---+--------+
>>> employee.filter(employee.id<3).show()
+---+---+--------+
|age| id|    name|
+---+---+--------+
| 25|  1|zhangsan|
| 28|  2|    lisi|
+---+---+--------+


6 spark SQL

在DataFrame上可以查询SQL。

6.1 连接spark SQL

类似其他的 Spark 程序库一样,在 Python 中不需要对构建方式修改。

6.2 在应用程序中使用spark SQL

6.2.1 初始化spark SQL

以使用hive数据库为例:

# 导入Spark SQL

from pyspark.sql import HiveContext, Row

# 当不能引入hive依赖时

from pyspark.sql import SQLContext, Row

示例


>>> from pyspark.sql import HiveContext, Row  # 本次使用的版本和配置不能导入HiveContext
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name HiveContext

>>> from pyspark.sql import SQLContext, Row  # 本次导入 SQLContext
>>> hivectx = SQLContext(sc)  # 创建上下文对象
>>> hivectx
<pyspark.sql.context.SQLContext object at 0x1924510>


6.2.2 spark SQL查询

要在一张数据表上进行查询,需要调用 HiveContext 或 SQLContext 中的 sql函数。

示例


>>> hivectx.sql('show tables')
>>> t = hivectx.sql('show tables')
>>> t.collect()
[Row(database=u'default', tableName=u't1', isTemporary=False)]


6.2.3 缓存查询结果

Spark SQL 的缓存机制与 Spark 中的稍有不同。由于我们知道每个列的类型信息,所以

Spark 可以更加高效地存储数据。为了确保使用更节约内存的表示方式进行缓存而不是储

存整个对象,应当使用专门的 hivectx.cacheTable("tableName") 方法。

7 spark sql 的增删改查

7.1 一个完整的例子:spark sql的增删改查

本次使用“本地hive元数据仓库”,没有配置hive-site.xml,导入SQLContext对象。

产生的表在Linux系统的默认路径是:/home/grid/spark-warehouse/

示例

在Linux Shell中启动pyspark:


[grid@master ~]$ spark3/bin/pyspark  # 在Linux Shell中,进入到pyspark
Python 2.7.5 (default, Aug  4 2017, 00:39:18) 
...
      ____              __
     / __/__  ___ _____/ /__
    _ / _ / _ `/ __/  '_/
   /__ / .__/_,_/_/ /_/_   version 3.0.0-preview
      /_/
Using Python version 2.7.5 (default, Aug  4 2017 00:39:18)
SparkSession available as 'spark'.


在pyspark中进行spark SQL的增删改查:


>>> from pyspark.sql import SQLContext  # 引入SQLContext
>>> hivectx = SQLContext(sc)  # 创建上下文环境
>>> a = hivectx.sql('create database test')  # 创建数据库
>>> a.take(1)
[]
>>> b = hivectx.sql('use test')  # 切换数据库
>>> b.take(1)
[]
>>> c = hivectx.sql('create table t1(id int ,name string)')  # 创建表
19/12/14 16:12:23 WARN HiveMetaStore: Location: file:
/home/grid/spark-warehouse/test.db/t1 specified for non-external table:t1
>>> c.take(1)
[]
>>> d = hivectx.sql('insert into t1 values (1, "zhangsan")')  # 插入数据
>>> d.take(1)
[]
>>> e = hivectx.sql('select * from t1')  # 查询数据
>>> e.take(10)
[Row(id=1, name=u'zhangsan')]


在linux shell中查看spark sql创建的数据库和表:


[grid@master spark-warehouse]$ ll /home/grid/spark-warehouse/ 
总用量 0
drwx------. 2 grid grid  6 12月 14 14:20 t1
drwx------. 3 grid grid 16 12月 14 16:12 test.db


8 总结

这是spark入门知识,关于RDD和DataFrame还有很多属性和方法,DataFrame还有Column等相关对象。

查看RDD的相关属性和方法


>>> type(lines)
<class 'pyspark.rdd.RDD'>
>>> [x for x in dir(lines) if not x.startswith('_')]
['aggregate', 'aggregateByKey', 'barrier', 'cache', 'cartesian', 'checkpoint', 'coalesce', 'cogroup', 'collect', 'collectAsMap', 'combineByKey', 'context', 'count', 'countApprox', 'countApproxDistinct', 'countByKey', 'countByValue', 'ctx', 'distinct', 'filter', 'first', 'flatMap', 'flatMapValues', 'fold', 'foldByKey', 'foreach', 'foreachPartition', 'fullOuterJoin', 'getCheckpointFile', 'getNumPartitions', 'getStorageLevel', 'glom', 'groupBy', 'groupByKey', 'groupWith', 'histogram', 'id', 'intersection', 'isCheckpointed', 'isEmpty', 'isLocallyCheckpointed', 'is_cached', 'is_checkpointed', 'join', 'keyBy', 'keys', 'leftOuterJoin', 'localCheckpoint', 'lookup', 'map', 'mapPartitions', 'mapPartitionsWithIndex', 'mapPartitionsWithSplit', 'mapValues', 'max', 'mean', 'meanApprox', 'min', 'name', 'partitionBy', 'partitioner', 'persist', 'pipe', 'randomSplit', 'reduce', 'reduceByKey', 'reduceByKeyLocally', 'repartition', 'repartitionAndSortWithinPartitions', 'rightOuterJoin', 'sample', 'sampleByKey', 'sampleStdev', 'sampleVariance', 'saveAsHadoopDataset', 'saveAsHadoopFile', 'saveAsNewAPIHadoopDataset', 'saveAsNewAPIHadoopFile', 'saveAsPickleFile', 'saveAsSequenceFile', 'saveAsTextFile', 'setName', 'sortBy', 'sortByKey', 'stats', 'stdev', 'subtract', 'subtractByKey', 'sum', 'sumApprox', 'take', 'takeOrdered', 'takeSample', 'toDF', 'toDebugString', 'toLocalIterator', 'top', 'treeAggregate', 'treeReduce', 'union', 'unpersist', 'values', 'variance', 'zip', 'zipWithIndex', 'zipWithUniqueId']


查看DataFrame的相关属性和方法


>>> type(employee)
<class 'pyspark.sql.dataframe.DataFrame'>
>>> [x for x in dir(employee) if not x.startswith('_')]
['agg', 'alias', 'approxQuantile', 'cache', 'checkpoint', 'coalesce', 'colRegex', 'collect', 'columns', 'corr', 'count', 'cov', 'createGlobalTempView', 'createOrReplaceGlobalTempView', 'createOrReplaceTempView', 'createTempView', 'crossJoin', 'crosstab', 'cube', 'describe', 'distinct', 'drop', 'dropDuplicates', 'drop_duplicates', 'dropna', 'dtypes', 'exceptAll', 'explain', 'fillna', 'filter', 'first', 'foreach', 'foreachPartition', 'freqItems', 'groupBy', 'groupby', 'head', 'hint', 'intersect', 'intersectAll', 'isLocal', 'isStreaming', 'is_cached', 'join', 'limit', 'localCheckpoint', 'mapInPandas', 'na', 'orderBy', 'persist', 'printSchema', 'randomSplit', 'rdd', 'repartition', 'repartitionByRange', 'replace', 'rollup', 'sample', 'sampleBy', 'schema', 'select', 'selectExpr', 'show', 'sort', 'sortWithinPartitions', 'sql_ctx', 'stat', 'storageLevel', 'subtract', 'summary', 'take', 'toDF', 'toJSON', 'toLocalIterator', 'toPandas', 'transform', 'union', 'unionAll', 'unionByName', 'unpersist', 'where', 'withColumn', 'withColumnRenamed', 'withWatermark', 'write', 'writeStream']