一、RDD的两种创建方式
Resilient Distributed Datasets (RDDs)
弹性 分布式 数据集
RDDS就是:弹性分布式数据集
参考:http://cwiki.apachecn.org/pages/viewpage.action?pageId=2885920
Spark 主要以一个弹性分布式数据集(RDD)的概念为中心,它是一个容错且可以执行并行操作的元素的集合。有两种方法可以创建 RDD : 在你的 driverprogram(驱动程序)中 parallelizing 一个已存在的集合,或者在外部存储系统中引用一个数据集,例如,一个共享文件系统,HDFS,HBase,或者提供 Hadoop InputFormat 的任何数据源。
第一种方法创建RDD:是在您的 driver program(驱动程序)中已存在的集合上通过调用 SparkContext 的 parallelize 方法来创建并行集合。
val rdd=sc.parallelize(Array(1,2,3,4,5)) //这句话的意思就是把该集合转化成rdd ,因为Spark不认集合,只认rdd,所以要转成RDD
rdd.collect().foreach(println) //collect返回的就是字符串类型,foreach()就是对集合里面的每一个元素做同一个事情,这里指打印print
第二种方法创建RDD: 可以使用 SparkContext 的 textFile 方法来创建文本文件的 RDD。此方法需要一个文件的 URI(计算机上的本地路径 ,hdfs://,s3n:// 等等的 URI),并且读取它们作为一个 lines(行)的集合
val rdd=sc.textFile("file:///e:/word.txt")
rdd.collect().foreach(println)
二、Spark-Shell
1、使用方法
在$SPARK_HOME目录的bin目录下
查看一下如何使用
[hadoop@hadoop001 bin]$ ./spark-shell --help
Usage: ./bin/spark-shell [options]
Options:
--master MASTER_URL spark://host:port, mesos://host:port, yarn,
k8s://https://host:port, or local (Default: local[*]).
--deploy-mode DEPLOY_MODE Whether to launch the driver program locally ("client") or
on one of the worker machines inside the cluster ("cluster")
(Default: client).
--class CLASS_NAME Your application's main class (for Java / Scala apps).
--name NAME A name of your application.
--jars JARS Comma-separated list of jars to include on the driver
and executor classpaths.
--packages Comma-separated list of maven coordinates of jars to include
on the driver and executor classpaths. Will search the local
maven repo, then maven central and any additional remote
repositories given by --repositories. The format for the
coordinates should be groupId:artifactId:version.
--exclude-packages Comma-separated list of groupId:artifactId, to exclude while
resolving the dependencies provided in --packages to avoid
dependency conflicts.
--repositories Comma-separated list of additional remote repositories to
search for the maven coordinates given with --packages.
--py-files PY_FILES Comma-separated list of .zip, .egg, or .py files to place
on the PYTHONPATH for Python apps.
--files FILES Comma-separated list of files to be placed in the working
directory of each executor. File paths of these files
in executors can be accessed via SparkFiles.get(fileName).
--conf PROP=VALUE Arbitrary Spark configuration property.
--properties-file FILE Path to a file from which to load extra properties. If not
specified, this will look for conf/spark-defaults.conf.
--driver-memory MEM Memory for driver (e.g. 1000M, 2G) (Default: 1024M).
--driver-java-options Extra Java options to pass to the driver.
--driver-library-path Extra library path entries to pass to the driver.
--driver-class-path Extra class path entries to pass to the driver. Note that
jars added with --jars are automatically included in the
classpath.
--executor-memory MEM Memory per executor (e.g. 1000M, 2G) (Default: 1G).
--proxy-user NAME User to impersonate when submitting the application.
This argument does not work with --principal / --keytab.
--help, -h Show this help message and exit.
--verbose, -v Print additional debug output.
--version, Print the version of current Spark.
Cluster deploy mode only:
--driver-cores NUM Number of cores used by the driver, only in cluster mode
(Default: 1).
Spark standalone or Mesos with cluster deploy mode only:
--supervise If given, restarts the driver on failure.
--kill SUBMISSION_ID If given, kills the driver specified.
--status SUBMISSION_ID If given, requests the status of the driver specified.
Spark standalone and Mesos only:
--total-executor-cores NUM Total cores for all executors.
Spark standalone and YARN only:
--executor-cores NUM Number of cores per executor. (Default: 1 in YARN mode,
or all available cores on the worker in standalone mode)
YARN-only:
--queue QUEUE_NAME The YARN queue to submit to (Default: "default").
--num-executors NUM Number of executors to launch (Default: 2).
If dynamic allocation is enabled, the initial number of
executors will be at least NUM.
--archives ARCHIVES Comma separated list of archives to be extracted into the
working directory of each executor.
--principal PRINCIPAL Principal to be used to login to KDC, while running on
secure HDFS.
--keytab KEYTAB The full path to the file that contains the keytab for the
principal specified above. This keytab will be copied to
the node running the Application Master via the Secure
Distributed Cache, for renewing the login tickets and the
delegation tokens periodically.
2、shell的使用
[hadoop@hadoop001 bin]$ ./spark-shell --master local[2]
18/12/30 13:02:31 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
Spark context Web UI available at http://hadoop001:4040
Spark context available as 'sc' (master = local[2], app id = local-1546146228136).
Spark session available as 'spark'.
启动后,试试通过parallelize读取并行集合
webui界面看到是2/2,是因为local[2],表示两个task,如果调整local[4],那么就有4个task
试试从外部文件读取数据
scala> sc.textFile("file:///home/hadoop/data/emp.txt").collect
读取hdfs上的文件
使用 Spark 来读取文件的一些注意事项 :
a. 如果使用本地文件系统的路径,所工作节点的相同访问路径下该文件必须可以访问。复制文件到所有工作节点上,或着使用共享的网络挂载文件系统。
b. 所有 Spark 中基于文件的输入方法,包括 textFile(文本文件),支持目录,压缩文件,或者通配符来操作。例如,您可以用 textFile("/my/directory"),textFile("/my/directory/*.txt") 和 textFile("/my/directory/*.gz")。
c. textFile 方法也可以通过第二个可选的参数来控制该文件的分区数量。默认情况下,Spark 为文件的每一个 block(块)创建的一个分区(HDFS 中块大小默认是 128M),当然你也可以通过传递一个较大的值来要求一个较高的分区数量。请注意,分区的数量不能够小于块的数量。
三、RDD的操作
有了rdd后,就会有相应的操作,主要有两大类
(1)、transformations(转换):在一个已存在的 dataset 上创建一个新的 dataset。
(2)、actions(动作): 将在 dataset 上运行的计算结果返回到驱动程序。
Spark 中的所有 transformations 都是 lazy(懒加载的),因此它不会立刻计算出结果。相反,他们只记得应用于一些基本数据集(例如 : 文件)的转换。只有当需要返回结果给驱动程序时,transformations 才开始计算。这种设计使 Spark 的运行更高效。
默认情况下,每次你在 RDD 运行一个 action 的时, 每个 transformed RDD 都会被重新计算。但是,您也可用 persist (或 cache) 方法将 RDD persist(持久化)到内存中;在这种情况下,Spark 为了下次查询时可以更快地访问,会把数据保存在集群上。此外,还支持持续持久化 RDDs 到磁盘,或复制到多个结点。
例子的应用:
1、transformations的简单运用
filter
scala> val a =sc.parallelize(1 to 10)
a: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[20] at parallelize at <console>:24
scala> a.filter(_%2==0)
res18: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[21] at filter at <console>:26
scala> a.filter(_%2==0).collect
res19: Array[Int] = Array(2, 4, 6, 8, 10)
scala> a.filter(_<5).collect
res20: Array[Int] = Array(1, 2, 3, 4)
map
scala> a.map(_*2).collect
res22: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
scala> a.map(_*2).filter(_>5).collect
res23: Array[Int] = Array(6, 8, 10, 12, 14, 16, 18, 20)
flatMap
scala> a.flatMap(x=>x.split("\t")).collect
res30: Array[String] = Array(hello, world, hello, hello, world, welcome)
scala> a.flatMap(x=>x.split("\t")).map((_,1)).collect
res31: Array[(String, Int)] = Array((hello,1), (world,1), (hello,1), (hello,1), (world,1), (welcome,1))
scala> a.flatMap(x=>x.split("\t")).map((_,1)).mapValues("x:"+ _).collect
res32: Array[(String, String)] = Array((hello,x:1), (world,x:1), (hello,x:1), (hello,x:1), (world,x:1), (welcome,x:1))
2、action的简单运用
对于很大的结果集,不要用collect算子。如果由必要看的话,可以考虑把结果存放到hdfs上,在hdfs上进行查看。或者用take算子取前几条来看。
scala> val a =sc.parallelize(1 to 10)
a: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[38] at parallelize at <console>:24
scala> a.take(4)
res33: Array[Int] = Array(1, 2, 3, 4)
scala> a.count
res34: Long = 10
scala> a.reduce(_+_)
res35: Int = 55
scala> a.first
res36: Int = 1
scala> a.top(3)
res38: Array[Int] = Array(10, 9, 8)
scala> a.foreach(println)
6
7
8
9
10
1
2
3
4
5
scala> a.sortBy(x=>x).collect
res40: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> a.sortBy(x=>x,false).collect
res41: Array[Int] = Array(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
案列
1. 统计每个域名的流量和
//step 1
val sparkConf=new SparkConf().setMaster("local[2]").setAppName("SparkContextApp") //必须要设置URL和名字
//step 2
val sc=new SparkContext(sparkConf)
//TODO....step 3
//需求1:统计每个域名的流量和,就要获取域名和流量这2个字段
val lines=sc.textFile("file:///E:/data.txt")
lines.map(x=>{
val temp=x.split("\t")
val domain=temp(0)
var response=0L
try{
response=temp(2).toLong
}catch {
case e:Exception=>println("......")
}
(domain,response) //这里是指返回值
}).reduceByKey(_+_).collect().foreach(println)
//step 4
sc.stop()
2、访问次数最多的URL
//step 1
val sparkConf=new SparkConf().setMaster("local[2]").setAppName("SparkContextApp") //必须要设置URL和名字
//step 2
val sc=new SparkContext(sparkConf)
//需求2:访问次数最多的url
val lines=sc.textFile("file:///E:/data.txt")
lines.map(x=>{
val temp=x.split("\t")
val domain=temp(0)
var response=0L
try{
response=temp(2).toLong
}catch {
case e:Exception=>println("......")
}
(domain,response) //这里是指返回值
}).reduceByKey(_+_).sortBy(_._2,false).take(10).foreach(println)
//step 4
sc.stop()
3、如何在服务器上提交
/*
* 域名0 ,流量2
* 输入:args(0)
* 输出:args(1)
* */
object LogServerApp {
//RDD编程的模型:第一步是创建sparkConf,第二步创建sparkContext,然后吧sparkConf传到sparkContext,第三业务代码,第四是关闭
def main(args: Array[String]): Unit = {
//最好就是在SparkContext创建之前,就创建sparkConf,跟着把sparkConf传到SparkContext
//step 1
val sparkConf=new SparkConf().setMaster("local[2]").setAppName("SparkContextApp") //必须要设置URL和名字
//step 2
val sc=new SparkContext(sparkConf)
//TODO....step 3
//需求1:统计每个域名的流量和,就要获取域名和流量这2个字段
//需求2:访问次数最多的url
val lines=sc.textFile(args(0)) //第一个参数作为输入
lines.map(x=>{
val temp=x.split("\t")
val domain=temp(0)
var response=0L
try{
response=temp(2).toLong
}catch {
case e:Exception=>println("......")
}
(domain,response) //这里是指返回值
}).reduceByKey(_+_).saveAsTextFile(args(1))
//step 4
sc.stop()
}
}
代码写完后,跟着
(1)打包IDEA中的代码
可能会下载。。。
最后打包完,会显示成功,和可以看到生成的jar包目录
(2)提交。来到Spark的官网,进入Overview下的Submitting Applications
或者在$SPARK_HOME/bin里面查查spark-submit的用法
a.将打包的路径E:\ruozedata_workspace\g5-spark\target\g5-spark-1.0.jar用命令rz上传到Lunux(服务器)上,
b.在hdfs上创建输入目录
hadoop fs -mkdir -p /logs/input
上传数据到/logs/input
c.运行脚本
./spark-submit \
--master local[2] \
--class com.ruozedata.bigdata.core02.LogServerApp \
--name LogServerApp \
/home/hadoop/lib/g5-spark-1.0.jar \
hdfs://hadoop001:9000/logs/input/ hdfs://hadoop001:9000/logs/output/
在hdfs查看一下结果,都正常
d.在工作中我们一般都封装脚本来执行的
${SPARK_HOME}/bin/spark-submit \
--master local[2] \
--class com.ruozedata.bigdata.core02.LogServerApp \
--name LogServerApp \
/home/hadoop/lib/g5-spark-1.0.jar \
hdfs://hadoop001:9000/logs/input/ hdfs://hadoop001:9000/logs/output/
然后把它保存到~shell/log.sh里面,修改一下执行权限,再执行即可。
chmod +x ./log.sh
./log.sh
(3)解决输出路径重复的问题
def main(args: Array[String]): Unit = {
//最好就是在SparkContext创建之前,就创建sparkConf,跟着把sparkConf传到SparkContext
//step 1
val sparkConf=new SparkConf().setMaster("local[2]").setAppName("SparkContextApp") //必须要设置URL和名字
//step 2
val sc=new SparkContext(sparkConf)
val uri=new URI("hdfs://hadoop001:9000")
val fileSystem=FileSystem.get(uri,sc.hadoopConfiguration)
if (fileSystem.exists(new Path(args(1)))){ //如果存在就删掉
fileSystem.delete(new Path(args(1)),true)
}
//TODO....step 3
//需求1:统计每个域名的流量和,就要获取域名和流量这2个字段
//需求2:访问次数最多的url
val lines=sc.textFile(args(0)) //第一个参数作为输入
lines.map(x=>{
val temp=x.split("\t")
val domain=temp(0)
var response=0L
try{
response=temp(2).toLong
}catch {
case e:Exception=>println("......")
}
(domain,response) //这里是指返回值
}).reduceByKey(_+_).saveAsTextFile(args(1))
//step 4
sc.stop()
}