Spark常用API(五)


【Task5】Spark常用API
spark集群搭建
初步认识Spark (解决什么问题,为什么比Hadoop快,基本组件及架构Driver/)
理解spark的RDD
使用shell方式操作Spark,熟悉RDD的基本操作
使用jupyter连接集群的pyspark
理解Spark的shuffle过程
学会使用SparkStreaming
说一说take,collect,first的区别,为什么不建议使用collect?
向集群提交Spark程序
使用spark计算《The man of property》中共出现过多少不重复的单词,以及出现次数最多的10个单词。
计算出movielen数据集中,平均评分最高的五个电影。
计算出movielen中,每个用户最喜欢的前5部电影
学会阅读Spark源码,整理Spark任务submit过程

参考资料:

远程连接jupyter

【没有jblas库解决办法】

下载jblas包 :https://pan.baidu.com/s/1o8w6Wem

运行spark-shell时添加jar:spark-shell --jars [jblas path] /jblas-1.2.4.jar

1. spark集群搭建

使用jupyter远程连接Spark集群

下载,解压缩并重命名

//解压spark
tar -zxvf spark-2.1.1-bin-hadoop2.7.tgz
//将压缩包删掉(可选)
rm -rf spark-2.1.1-bin-hadoop2.7.tgz
//改名为spark
mv spark-2.1.1-bin-hadoop2.7 hadoop

前期基本的spark集群我们已经搭建好了,现在是要使用jupyter远程连接Spark集群,默认Centos7中是有python安装的,但是2.7版本,我们需要安装py3。我们去看一下默认的py2.7在哪里。

修改配置文件

vi ~/.bashrc
//下方是修改内容,添加SPARK_HOME,在PATH里添加路径
export SPARK_HOME=/usr/local/spark
export PATH=$PATH:$JAVA_HOME/bin:$HIVE_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$ZOOKEEPER_HOME/bin:$SPARK_HOME/bin
//使得配置生效
source ~/.bashrc

cd到 /spark/conf目录下,执行如下操作

//复制spark-env.sh.template成spark-env.sh
cp spark-env.sh.template spark-env.sh

//修改spark-env.sh,添加如下内容
export HADOOP_HOME=/usr/local/hadoop
export HADOOP_CONF_DIR=/usr/local/hadoop/etc/hadoop
export JAVA_HOME=/usr
# 设置Master的主机名
export SPARK_MASTER_IP=192.168.1.105
export SPARK_LOCAL_IP=192.168.1.105
export SPARK_MASTER_HOST=192.168.1.105
# 提交Application的端口,默认就是这个,万一要改呢,改这里
export SPARK_MASTER_PORT=7077
# 每一个Worker最多可以使用的cpu core的个数,我虚拟机就一个...
# 真实服务器如果有32个,你可以设置为32个
export SPARK_WORKER_CORES=1
# 每一个Worker最多可以使用的内存,我的虚拟机就2g
# 真实服务器如果有128G,你可以设置为100G
export SPARK_WORKER_MEMORY=1g
export SCALA_HOME=/usr/local/scala
export SPARK_HOME=/usr/local/spark
export SPARK_DIST_CLASSPATH=$(/usr/local/hadoop/bin/hadoop classpath)

复制slaves.template成slaves,并修改内容

cp slaves.template slaves
# 修改slaves,添加如下内容
spark1
spark2
spark3
这里同时将里面的localhost删去

将配置好的spark文件复制到Slave1和Slave2节点。

scp -r /usr/local/spark root@spark2:/usr/local
scp -r /usr/local/spark root@spark3:/usr/local

修改spark2/spark3的配置

在CP2和CP3上分别修改~/.bashrc,增加Spark的配置,过程同Master一样。
在CP2和CP3修改spark-env.sh,将export SPARK_LOCAL_IP=xxx.xxx.xxx.xxx改成spark1和spark2对应节点的IP

在Master节点启动集群,(start-all.sh会与hadoop冲突,所以这里加上路径)

/usr/local/spark/sbin/start-all.sh

查看集群是否启动成功

jps

spark1在Hadoop的基础上新增了:Master和Worker:

10066 NameNode
17269 Kafka
30694 Worker
10248 SecondaryNameNode
10392 ResourceManager
30680 Worker
30936 Jps
16589 QuorumPeerMain
30575 Master

spark2、spark3在Hadoop的基础上新增了:Worker

2. 初步认识Spark

(解决什么问题,为什么比Hadoop快,基本组件及架构Driver/)

1.背景:

在spark出现之前,hadoop的迅速发展,hadoop分布式集群,把编程简化为自动提供 位置感知性调度,容错,以及负载均衡的一种模式,用户就可以在普通的PC机上运行超大集群运算,hadoop有一个非常大的问题:hadoop是基于流处理的,hadoop会从(物理存储)hdfs中加载数据,然后处理之后再返回给物理存储hdfs中,这样不断的读取与写入,占用了大量的IO,后来hadoop出现了非循环的数据流模型,也就是DAG,但是其中任然出现了两个重大的问题:

1.任然是不断的重复写入和读取磁盘。每次操作都要完成这两步,太浪费了。

3.交互式数据查询。比如:用户不断查询具体的一个用户的子集。

2.比如,机器学习,图计算,数据挖掘方面不适用,现在要做大量的重复操作,并且下一次的开始,要依据前面计算的结果,这样对于hadoop来说就要重新的计算,从而浪费大量的资源。

2.Spark到底解决了什么根本性的技术问题?

基于上述:

spak提出了分布式的内存抽象,RDD(弹性分布式数据集)支持工作集的应用,也具有数据流模型的特点,例如,自动容错,位置感知,可伸缩性和可扩展性,并且RDD支持多个查询时,显示的将工作集缓存到内存中,后续查询时能够重用工作集的结果。这样与hadoop相比,就极大的提高了速度。

RDD提供了共享内存模型,RDD本身只记录分区的集合,只能通过其他的RDD通过转换例如,map,join等操作来创建新的RDD,而RDD并不需要检查点操作,为什么?因为前后之间的RDD是有”血统”关系的,其核心原因是,每个RDD包含了从其他RDD计算出分区的所有内容,并且这个计算不是从头开始计算,而是仅仅指的是从上一步开始计算得到即可,这也就实现了工作集的复用。

Spark周围的SQL,机器学习,图计算都是基于此构建出来的,使得Spark成为一体化的大数据平台,不仅降低了各个开发,运维的成本,也提高了性能。

参考:https://blog.csdn.net/snail_gesture/article/details/49688569

https://blog.csdn.net/zisheng_wang_DATA/article/details/50555860

https://blog.csdn.net/dashujuedu/article/details/53487199

Spark为什么快?

1、消除了冗余的HDFS读写

Hadoop每次shuffle操作后,必须写到磁盘,而Spark在shuffle后不一定落盘,可以cache到内存中,以便迭代时使用。如果操作复杂,很多的shufle操作,那么Hadoop的读写IO时间会大大增加。

2、消除了冗余的MapReduce阶段

Hadoop的shuffle操作一定连着完整的MapReduce操作,冗余繁琐。而Spark基于RDD提供了丰富的算子操作,且reduce操作产生shuffle数据,可以缓存在内存中。

3、JVM的优化

Hadoop每次MapReduce操作,启动一个Task便会启动一次JVM,基于进程的操作。而Spark每次MapReduce操作是基于线程的,只在启动Executor是启动一次JVM,内存的Task操作是在线程复用的。

每次启动JVM的时间可能就需要几秒甚至十几秒,那么当Task多了,这个时间Hadoop不知道比Spark慢了多少。

考虑一种极端查询:Select month_id,sum(sales) from T group by month_id;

这个查询只有一次shuffle操作,此时,也许Hive HQL的运行时间也许比Spark还快。

结论:Spark快不是绝对的,但是绝大多数,Spark都比Hadoop计算要快。这主要得益于其对mapreduce操作的优化以及对JVM使用的优化。

参考:https://blog.csdn.net/lmalds/article/details/51189924

spark的四大核心组件

在这里插入图片描述
相对于第一代的大数据生态系统Hadoop中的MapReduce,Spark 无论是在性能还是在方案的统一性方面,都有着极大的优势。Spark框架包含了多个紧密集成的组件,如图4所示。位于底层的是Spark Core,其实现了Spark的作业调度、内存管理、容错、与存储系统交互等基本功能,并针对弹性分布式数据集提供了丰富的操作。在Spark Core的基础上,Spark提供了一系列面向不同应用需求的组件,主要有Spark SQL、Spark Streaming、MLlib、GraphX。

1.Spark SQL

Spark SQL是Spark用来操作结构化数据的组件。通过Spark SQL,用户可以使用SQL或者Apache Hive版本的SQL方言(HQL)来查询数据。Spark SQL支持多种数据源类型,例如Hive表、Parquet以及JSON等。Spark SQL不仅为Spark提供了一个SQL接口,还支持开发者将SQL语句融入到Spark应用程序开发过程中,无论是使用Python、Java还是Scala,用户可以在单个的应用中同时进行SQL查询和复杂的数据分析。由于能够与Spark所提供的丰富的计算环境紧密结合,Spark SQL得以从其他开源数据仓库工具中脱颖而出。Spark SQL在Spark l.0中被首次引入。在Spark SQL之前,美国加州大学伯克利分校曾经尝试修改Apache Hive以使其运行在Spark上,进而提出了组件Shark。然而随着Spark SQL的提出与发展,其与Spark引擎和API结合得更加紧密,使得Shark已经被Spark SQL所取代。

2.Spark Streaming

众多应用领域对实时数据的流式计算有着强烈的需求,例如网络环境中的网页服务器日志或是由用户提交的状态更新组成的消息队列等,这些都是实时数据流。Spark Streaming是Spark平台上针对实时数据进行流式计算的组件,提供了丰富的处理数据流的API。由于这些API与Spark Core中的基本操作相对应,因此开发者在熟知Spark核心概念与编程方法之后,编写Spark Streaming应用程序会更加得心应手。从底层设计来看,Spark Streaming支持与Spark Core同级别的容错性、吞吐量以及可伸缩性。

3.MLlib

MLlib是Spark提供的一个机器学习算法库,其中包含了多种经典、常见的机器学习算法,主要有分类、回归、聚类、协同过滤等。MLlib不仅提供了模型评估、数据导入等额外的功能,还提供了一些更底层的机器学习原语,包括一个通用的梯度下降优化基础算法。所有这些方法都被设计为可以在集群上轻松伸缩的架构。

4.GraphX

GraphX是Spark面向图计算提供的框架与算法库。GraphX中提出了弹性分布式属性图的概念,并在此基础上实现了图视图与表视图的有机结合与统一;同时针对图数据处理提供了丰富的操作,例如取子图操作subgraph、顶点属性操作mapVertices、边属性操作mapEdges等。GraphX还实现了与Pregel的结合,可以直接使用一些常用图算法,如PageRank、三角形计数等。

上述这些Spark核心组件都以jar包的形式提供给用户,这意味着在使用这些组件时,与MapReduce上的Hive、Mahout、Pig等组件不同,无需进行复杂烦琐的学习、部署、维护和测试等一系列工作,用户只要搭建好Spark平台便可以直接使用这些组件,从而节省了大量的系统开发与运维成本。将这些组件放在一起,就构成了一个Spark软件栈。基于这个软件栈,Spark提出并实现了大数据处理的一种理念——“一栈式解决方案(one stack to rule them all)”,即Spark可同时对大数据进行批处理、流式处理和交互式查询,如图5所示。借助于这一软件栈用户可以简单而低耗地把各种处理流程综合在一起,充分体现了Spark的通用性。
在这里插入图片描述
参考:https://blog.csdn.net/c36qUCnS2zuqF6/article/details/81518150

https://blog.csdn.net/mys_35088/article/details/81020542

3. 理解spark的RDD

参考:https://blog.csdn.net/u011094454/article/details/78992293

https://blog.csdn.net/guohecang/article/details/51736572

4. 使用shell方式操作Spark,熟悉RDD的基本操作

RDD的操作分为两种,一种是转化操作,一种是执行操作,转化操作并不会立即执行,而是到了执行操作才会被执行。

转化操作

map() 参数是函数,函数应用于RDD每一个元素,返回值是新的RDD

flatMap() 参数是函数,函数应用于RDD每一个元素,将元素数据进行拆分,变成迭代器,返回值是新的RDD

filter() 参数是函数,函数会过滤掉不符合条件的元素,返回值是新的RDD

distinct() 没有参数,将RDD里的元素进行去重操作

union() 参数是RDD,生成包含两个RDD所有元素的新RDD

intersection() 参数是RDD,求出两个RDD的共同元素

subtract() 参数是RDD,将原RDD里和参数RDD里相同的元素去掉

cartesian() 参数是RDD,求两个RDD的笛卡儿积

行动操作

collect() 返回RDD所有元素

count() RDD里元素个数

countByValue() 各元素在RDD中出现次数

reduce() 并行整合所有RDD数据,例如求和操作

fold(0)(func) 和reduce功能一样,不过fold带有初始值

aggregate(0)(seqOp,combop) 和reduce功能一样,但是返回的RDD数据类型和原RDD不一样

foreach(func) 对RDD每个元素都是使用特定函数

行动操作每次的调用时不会存储前面的计算结果的,若果想要存储前面的操作结果需要把结果加载需要在需要缓存中间结果的RDD调用cache(),cache()方法是把中间结果缓存到内存中,也可以指定缓存到磁盘中(也可以只用persisit())

参考:https://blog.csdn.net/HANLIPENGHANLIPENG/article/details/53508746

输入spark-shell进入spark交互式环境中。RDDs可以使用Hadoop InputFormats(例如HDFS文件)创建,也可以从其他的RDDs转换。让我们在Spark源代码目录里从README.md文本文件中创建一个新的RDD。

Spark Shell启动时遇到:14: error: not found: value spark import spark.implicits._ :14: error: not found: value spark import spark.sql错误的解决办法(图文详解)

Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.1.1
      /_/
         
Using Scala version 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_212)
Type in expressions to have them evaluated.
Type :help for more information.
scala> val textFile = sc.textFile("file:///usr/local/spark/README.md")
textFile: org.apache.spark.rdd.RDD[String] = file:///usr/local/spark/README.md MapPartitionsRDD[1] at textFile at <console>:24

file:///usr/local/spark/README.md,首部的file代表本地目录,注意file:后有三个斜杠(/);中间加粗部分是我的spark安装目录。

RDD的actions从RDD中返回值,transformations可以转换成一个新RDD并返回它的引用。下面展示几个action:

scala> textFile.count()
res0: Long = 104

scala> textFile.first()
res1: String = # Apache Spark

其中,count代表RDD中的总数据条数;first代表RDD中的第一行数据。

下面使用一个transformation,我们将使用filter函数对textFile这个RDD进行过滤,取出包含字符串"Spark"的行,并返回一个新的RDD:

scala> val linesWithSpark = textFile.filter(line => line.contains("Spark"))
linesWithSpark: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[2] at filter at <console>:26

当然也可以把actions和transformations连接在一起使用:
上面这条语句表示有多少行包括字符串"Spark"。

更多RDD操作

RDD actions和transformations能被用在更多的复杂计算中。比如想要找到一行中最多的单词数量:

scala> textFile.map(line => line.split(" ").size).reduce((a, b) => if (a > b) a else b)
res3: Int = 22

首先将行映射成一个整型数值产生一个新的RDD。在这个新的RDD上调用reduce找到行中最大的单词数个数。map和reduce的参数是Scala的函数串(闭包),并且可以使用任何语言特性或者Scala/Java类库。例如,我们可以很方便地调用其他的函数声明。我们使用Math.max()函数让代码更容易理解:

scala> import java.lang.Math
import java.lang.Math

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

大家都知道,Hadoop流行的一个通用数据流模式是MapReduce。Spark能够很容易地实现MapReduce:

scala> val wordCounts = textFile.flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey((a, b) => a + b)
wordCounts: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[8] at reduceByKey at <console>:27

这里,我们结合了flatMap、map和reduceByKey来计算文件里每个单词出现的数量,它的结果是包含一组(String, Int)键值对的RDD。我们可以使用collect操作收集单词的数量:

scala> wordCounts.collect()
res5: Array[(String, Int)] = Array((package,1), (For,3), (Programs,1), (processing.,1), (Because,1), (The,1), 
(page](http://spark.apache.org/documentation.html).,1), (cluster.,1), (its,1), ([run,1), (than,1), (APIs,1), 
(have,1), (Try,1), (computation,1), (through,1), (several,1), (This,2), (graph,1), (Hive,2), (storage,1), 
(["Specifying,1), (To,2), ("yarn",1), (Once,1), (prefer,1), (SparkPi,2), (engine,1), (version,1), (file,1), 
(documentation,,1), (processing,,1), (the,24), (are,1), (systems.,1), (params,1), (not,1), (different,1), (refer,2), 
(Interactive,2), (R,,1), (given.,1), (if,4), (build,4), (when,1), (be,2), (Tests,1), (Apache,1), (thread,1), (programs,,1), 
(including,4), (./bin/run-example,2), (Spark.,1), (package.,1), (1000).count(),1), (Versions,1), (HDFS,1), (Data.,1), (>>>,...

缓存

Spark支持把数据集缓存到内存之中,当要重复访问时,这是非常有用的。举一个简单的例子:

scala> linesWithSpark.cache()
res6: linesWithSpark.type = MapPartitionsRDD[2] at filter at <console>:26

scala> linesWithSpark.count()
res7: Long = 20

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

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

先缓存linesWithSpark数据集,然后重复访问count函数返回的值。当然,我们并不能察觉明显的查询速度变化,但是当在大型的数据集中使用缓存,将会非常显著的提升相应的迭代操作速度。

参考:https://blog.csdn.net/universe_ant/article/details/52014068

5. 使用jupyter连接集群的pyspark

Spark伪分布式环境搭建 + jupyter连接spark集群
备份Python2.7

[root@spark1 ~]# cd /usr/
[root@spark1 usr]# cd bin
[root@spark1 bin]# ls python*

python python2 python2.7

三个显示结果中最后一个是python2.7,实际上这几个文件之间是有依赖关系的。在 ls 后面加个 -al参数,如下:

[root@spark1 bin]# ls -al  python*

lrwxrwxrwx. 1 root root 7 Jul 24 21:39 python -> python2
lrwxrwxrwx. 1 root root 9 Jul 24 21:39 python2 -> python2.7
-rwxr-xr-x. 1 root root 7216 Oct 30 2018 python2.7

依赖关系很明显就可以看到。我们要安装版本3,首先要把刚才显示的三个python文件中的第一个python给备份一下(不保留源文件,仅保留备份文件就可以)

[root@spark1 bin]# mv python python.bak

安装python3
先在官网下载python3的压缩包,然后在指定的文件夹里进行解压。

cd /usr/bin/
tar -xvzf  Python-3.6.9.tgz
cd Python-3.6.9
./configure --prefix=/usr/local/python3.6.9
make
make install

我在这里安装过程中遇到一些错误,主要是两点,其一是要在 /usr/bin/yum 的文件里,将第一行的python版本进行修改,这里我直接改为python2.7,yum默认是python2的;其二是要把下载器中的python也修改对应的版本。

vi /usr/libexec/urlgrabber-ext-down

将文件头部的
#!/usr/bin/python

改成
#!/usr/bin/python2.7

下面是遇到错误后我找的一些解决方法。

Linux下切换Python版本

Python3.6安装报错 configure: error: no acceptable C compiler found in $PATH

centos7 yum报错Error downloading packages:

yum出现Error downloading packages错误

Python安装常见问题(1):zipimport.ZipImportError: can’t decompress data

yum -y install zlib*
vim Module/Setup

找到下面这一行
#zlib zlibmodule.c -I$(prefix)/include -L$(exec_prefix)/lib -lz
去掉注释
     zlib zlibmodule.c -I$(prefix)/include -L$(exec_prefix)/lib -lz

cd ..
make && make install

终于安装成功!

Anaconda安装

Linux Centos7安装anaconda3和jupyter
清华大学开源软件镜像站

6. 理解Spark的shuffle过程

MapReduce中的shuffle机制

在MapReduce框架中,shuffle是连接Map和Reduce之间的桥梁,Map的输出要用到Reduce中必须经过shuffle这个环节,shuffle的性能高低直接影响了整个程序的性能和吞吐量。

Shuffle是MapReduce框架中的一个特定的phase,介于Map phase和Reduce phase之间,当Map的输出结果要被Reduce使用时,输出结果需要按key哈希,并且分发到每一个Reducer上去,这个过程就是shuffle。由于shuffle涉及到了磁盘的读写和网络的传输,因此shuffle性能的高低直接影响到了整个程序的运行效率。

什么是Spark Shuffle?

比如: reduceByKey是一个聚合类的函数(reduceByKey包括groupByKey和reduce),当使用reduceByKey时会产生Shuffle过程,reduceByKey会将上一个RDD中的每一个key对应的所有value聚合成 一个value,然后生成一个新的RDD,元素类型是对的形 式,这样每一个key对应一个聚合起来的value。

Spark中的Shuffle是把一组无规则的数据尽量转换成一组具有一定规则的数据。

Spark Shuffle产生的问题

每一个key对应的value不一定都是在一个partition中 ,也不太可能在同一个节点上,因为RDD是分布式的弹性的数据集,它的partition极有可能分布在各个节点上。

既然出现如上的问题,那么Spark如何进行聚合?

– Shuffle Write:上一个stage的每个map task就必须保证将自己处理 的当前分区中的数据相同的key写入一个分区文件中,可能会写入多个不同的分区文件中。

– Shuffle Read:reduce task就会从上一个stage的所有task所在的机器上寻找属于自己的那些分区文件,这样就可以保证每一个key所对应的value都会汇聚到同一个节点上去处理和聚合。

参考:Spark学习笔记——spark shuffle过程

https://blog.csdn.net/databatman/article/details/53023818

7. 学会使用SparkStreaming

什么是Spark Streaming?

首先,什么是流(streaming)?数据流是连续到达的无穷序列。流处理将不断流动的输入数据分成独立的单元进行处理。流处理是对流数据的低延迟处理和分析。Spark Streaming是Spark API核心的扩展,可实现实时数据的快速扩展,高吞吐量,高容错处理。Spark Streaming适用于大量数据的快速处理。

Spark Streaming支持如HDFS目录,TCP套接字,Kafka,Flume,Twitter等数据源。数据流可以用Spark 的核心API,DataFrames SQL,或机器学习的API进行处理,并且可以被保存到HDFS,databases或Hadoop OutputFormat提供的任何文件系统中去。

Spark Straming如何工作

Spark Streaming将数据流每X秒分作一个集合,称为Dstreams,它在内部是一系列RDD。您的Spark应用程序使用Spark API处理RDD,并且批量返回RDD操作的结果。

编写 Spark Streaming 程序的基本步骤是

通过创建输入DStream来定义输入源;
通过对 DStream 应用的 转换操作 和 输出操作 来定义流计算;
用streamingContext.start()来开始接收数据和处理流程;
通过streamingContext.awaitTermination()方法来等待处理结束(手动结束或因为错误而结束);
可以通过streamingContext.stop()来手动结束流计算进程。
本例是创建一个实时的wordcount程序,程序监听TCP套接字的数据服务器获取文本数据,然后计算文本中包含的单词数。本例是在spark-shell中实现的,如果想编写独立的应用程序会稍有不同。在编写程序之前我们要先运行Netcat作为数据服务器,比如你可以在master节点运行以下命令:

nc –lk 9999

如果你没有安装nc,需要运行下列命令安装nc:

# Removes the old package
yum erase nc

# Manually downloads the working package from the Official Repository
wget http://vault.centos.org/6.6/os/x86_64/Packages/nc-1.84-22.el6.x86_64.rpm

# Installs the package
rpm -iUv nc-1.84-22.el6.x86_64.rpm

如果你跟我一样找不到yum(原因是之前把python2的软链接改了),那么:

vi /usr/bin/yum
# 将第一行的/usr/bin/python改成/usr/bin/python2

然后安装nc,并运行nc –lk 9999命令。
时下面会有一个光标在闪动。

然后开第二个终端,在spark-shell中输入 :paste 回车,冒号也要输入,进入粘贴模式。

然后复制下面的代码:

import org.apache.spark._
import org.apache.spark.streaming._
import org.apache.spark.streaming.StreamingContext._
val ssc = new StreamingContext(sc, Seconds(1))
val lines = ssc.socketTextStream("master", 9999)
 
val words = lines.flatMap(_.split(" "))
val pairs = words.map(word => (word, 1))
val wordCounts = pairs.reduceByKey(_ + _)
// Print the first ten elements of each RDD generated in this DStream to the console
wordCounts.print()
 
ssc.start()             // Start the computation
ssc.awaitTermination()  // Wait for the computation to terminate

按下ctrl+D执行这段代码。这时会不断打印出处理结果,每1s打印一次。

这时我们在第一个终端输入hello word,回车。看到第二个终端结果中出现(hello,1),(world,1),成功。

使用的过程中遇到错误,参考启动 ./spark-shell 命令报错解决了,不过我不敢删除文件夹,就把文件夹重命名了。

参考:腾讯云技术社区:Spark Streaming入门

使用 Spark Streaming 进行实时流计算(一)

https://blog.csdn.net/u013468917/article/details/51249711

8. 说一说take,collect,first的区别,为什么不建议使用collect?

first:返回第一个元素

scala> val rdd = sc.parallelize(List(1,2,3,3))

scala> rdd.first()
res1: Int = 1

take:rdd.take(n)返回第n个元素

scala> val rdd = sc.parallelize(List(1,2,3,3))

scala> rdd.take(2)
res3: Array[Int] = Array(1, 2)

collect:rdd.collect() 返回 RDD 中的所有元素

scala> val rdd = sc.parallelize(List(1,2,3,3))

scala> rdd.collect()
res4: Array[Int] = Array(1, 2, 3, 3)

Spark内有collect方法,是Action操作里边的一个算子,这个方法可以将RDD类型的数据转化为数组,同时会从远程集群是拉取数据到driver端。

首先,collect是Action里边的,根据RDD的惰性机制,真正的计算发生在RDD的Action操作。那么,一次collect就会导致一次Shuffle,而一次Shuffle调度一次stage,然而一次stage包含很多个已分解的任务碎片Task。这么一来,会导致程序运行时间大大增加,属于比较耗时的操作,即使是在local模式下也同样耗时。

其次,从环境上来讲,本机local模式下运行并无太大区别,可若放在分布式环境下运行,一次collect操作会将分布式各个节点上的数据汇聚到一个driver节点上,而这么一来,后续所执行的运算和操作就会脱离这个分布式环境而相当于单机环境下运行,这也与Spark的分布式理念不合。

最后,将大量数据汇集到一个driver节点上,并且像这样val arr = data.collect(),将数据用数组存放,占用了jvm堆内存,可想而知,是有多么轻松就会内存溢出。

官方说法:

打印一个弹性分布式数据集元素,使用时要注意不要导致内存溢出!

建议使用 take(): rdd.take(100).foreach(println),

而不使用rdd.collect().foreach(println)。

因为后者会导致内存溢出!!

(spark collect操作的特点是从远程集群是拉取数据到本地,经过网络传输,如果数据量的话,会给网络造成很大的压力,和foreach的区别是,foreach是在远程集群上遍历rdd中的元素,如果是在本地的话,差别不大。建议使用foreach,不要用collect.)

因此:

若需要遍历RDD中元素,大可不必使用collect,可以使用foreach语句;

若需要打印RDD中元素,可用take语句,返回数据集前n个元素

参考:https://blog.csdn.net/T1DMzks/article/details/70667011

https://blog.csdn.net/high2011/article/details/53138279

https://blog.csdn.net/chaoshengmingyue/article/details/82021746

9. 向集群提交Spark程序

(1)在集群中运行应用程序JAR包

向独立集群管理器提交应用,需要把spark://master:7077作为主节点参数递给spark-submit。下面我们可以运行Spark安装好以后自带的样例程序SparkPi,它的功能是计算得到pi的值(3.1415926)。

到spark的安装目录下的examples/jars路径下查看自带的样例程序SparkPi:

[root@CP1 jars]# ll
total 2052
-rw-r--r--. 1 500 500  121970 Apr 25  2017 scopt_2.11-3.3.0.jar
-rw-r--r--. 1 500 500 1975967 Apr 25  2017 spark-examples_2.11-2.1.1.jar

然后回到spark目录下,运行下列代码:

bin/spark-submit --class org.apache.spark.examples.SparkPi --master spark://master:7077 examples/jars/spark-examples_2.11-2.1.1.jar 100 2>&1 | grep "Pi is roughly"

2)在集群中运行pyspark

也可以用spark-shell连接到独立集群管理器上。

首先做一点准备工作,把一个README.md文件拷贝到HDFS上,用于后面的测试。

cd /usr/local/hadoop/
# 下面这条命令中,我们把spark安装目录下的README.md文件上传到分布式文件系统HDFS的根目录下
bin/hadoop fs -put /usr/local/spark/README.md /

在Shell中输入如下命令启动进入pyspark:

cd /usr/local/spark/
bin/pyspark --master spark://master:7077

当然,我运行到这就进入jupyter notebook了,因为之前配置过。如果你在pyspark交互式界面,可以输入以下命令测试(还记得嘛,我在scala界面测试过):

>>> textFile = sc.textFile("hdfs://master:9000/README.md")
>>> textFile.count()
99                                                                 
>>> textFile.first()
# Apache Spark

Hadoop YARN管理器

(1)在集群中运行应用程序JAR包
向Hadoop YARN集群管理器提交应用,需要把yarn-cluster作为主节点参数递给spark-submit。
请登录Linux系统,打开一个终端,在Shell中输入如下命令:

bin/spark-submit --class org.apache.spark.examples.SparkPi --master yarn-cluster examples/jars/spark-examples_2.11-2.1.1.jar

然后会出现一个tracking URL,复制该URL到浏览器,点击查看Logs,再点击stdout,即可查看结果

参考:

在集群上运行Spark应用程序(Python版)_厦大数据库实验室博客

10. 使用spark进行计算

《The man of property》中共出现过多少不重复的单词,以及出现次数最多的10个单词。

# 设置数据的路径
textData = sc.textFile("file:///usr/local/liling/TheManOfProperty.txt")

# 将文本数据按行处理,每行按空格拆成一个数组,flatMap会将各个数组中元素合成一个大的集合
splitData = textData.flatMap(lambda line:line.split(" "))

# 处理合并后的集合中的元素,每个元素的值为1,返回一个元组(key,value)
# 其中key为单词,value这里是1,即该单词出现一次
flagData = splitData.map(lambda word:(word,1))

# reduceByKey会将textSplitFlag中的key相同的放在一起处理
# 传入的(x,y)中,x是上一次统计后的value,y是本次单词中的value,即每一次是x+1
countData = flagData.reduceByKey(lambda x,y:x+y)

countData.count()
# 输出:18344

因为之前配置过了,进入pyspark后已经有sc环境,所以直接使用就可以。可以看到不重复的单词有18344个,应该是对的吧。

出现次数最多的10个单词

result = countData.sortBy(lambda x: x[1], False).take(10)
result

输出:

[('the', 5229),
 ('of', 3496),
 ('', 3189),
 ('to', 2824),
 ('and', 2593),
 ('a', 2512),
 ('he', 2069),
 ('his', 1902),
 ('in', 1733),
 ('was', 1695)]

参考:

https://blog.csdn.net/proplume/article/details/79798289

python spark 通过key来统计不同values个数

pyspark进行词频统计并返回topN - Sea_Sky - 博客园

11. 计算出movielen数据集中,平均评分最高的五个电影

12. 计算出movielen中,每个用户最喜欢的前5部电影

13. 学会阅读Spark源码,整理Spark任务submit过程

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值