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是不可变的、分布式的数据集合。
- RDD会被划分为多个分区,运行在集群的不同节点。
- RDD的数据类型可以是java、scala、python的数据类型,也可以是用户自定义的。
- 定义RDD时,spark会惰性计算这些值。只有spark开始转化操作时,了解到完整的数据转化链,才会去计算,计算真正需求的数据。
- 每当我们调用一个新的行动操作时,整个 RDD 都会从头开始计算。要避免这种低效的行为,用户可以将中间结果持久化。
- 如果不重用 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']