val lines = sc.textFile("data.txt") val lineLengths = lines.map(s => s.length) val totalLength = lineLengths.reduce((a, b) => a + b)
spark是懒惰运行的,上面两行都不会直接执行,直到要开始执行reduce 这个action的时候才会执行
明白闭包 closure
以一个例子作为说明
val counter = 0 val rdd = sc.parallelize(data) rdd.foreach(x => counter += x) println("counter value= " + counter)
rdd.foreach(x => println(x))
在计算rdd.foreach的时候,rdd被split到了不同的计算节点上,在此之前spark会计算出来闭包,然后将闭包传递给每个Node进行计算。这个闭包当中就包含了全局变量counter的副本,那么在集群上修改的仅仅是副本,最终driver中的counter是不会被改变的。如果在单机上运行,也取决于是否运行于同一个JVM当中,如果在一个JVM当中,则可能侥幸会更新全局counter。这种想在闭包内操作闭包外变量的行为在spark里是未定义的。对于这种情况,可以使用Accumulator
所以,不要在rdd的操作中修改全局变量
另一个 rdd.foreach(x => println(x)) 也是不行的,在分布式情况下,stdout是各个executor的stdout,driver是不会输出任何东西的,可以用rdd.collect.foreach(x => println(x)),但这有一个严重的问题,可能OOM,如果只是看其中一部分,可以使用rdd.take(100).foreach(x => println(x))
定义函数
//完整的函数定义
def f1(i:Int) : String = { "value=" + i }
// 省略返回值和等号
def f2(i:Int){
println(i)
}
// 省略了参数和返回值定义
def f3 ={
"111"
}
// f4等于一个匿名函数
def f4 = () =>{
"fsfsf"
}
Functions & Evaluations
1、如果CBV 能终止,那么CBN也能终止。
2、反之不然
def first(x: Int, y: Int) = x first(1, loop) CBV = 1 first(1, loop) CBN = 无限循环
The difference between val and def becomes apparent when the right hand side does not terminate. Given
def loop: Boolean = loop def x = loop // 只是给loop 起了另外一个名字,OK val x = loop // BAD,val会在复制的那一刻计算表达式
测试scala程序
写完一个scala之后,sbt,进入sbt命令行模式
> pachage //打包 > run //运行程序 从main函数开始运行,或者object 继承(extends)了App,这样就不用定义main函数
或者在sbt console里面
sbt console scala > import example.session._ scala > sqrt_self(4)
函数
Int => Int // 就是一个参数类型为Int 返回值也为 Int的匿名函数 def sum(f: Int => Int, a: Int, b: Int): Int = { def loop(a: Int, acc: Int):Int = { if (a > b) acc else loop(a+1, f(a) + acc) } loop(a, 0) } // a到b的 f 和,tail recursion sum(x => x * x, 3, 5)
数据的共享内存处理和分布式处理的区别
共享内存处理:是一份数据会被在内存中切成若干块,然后被若干个thread处理,最后合并起来
分布式处理:是将一份数据分成若干块,放在不同的机器上面(机器取代了共享内存),每个机器可以成为一个Node(Node取代了thread),每台机器单独处理,最后合并起来
由此引发的一个问题:分布式处理需要考虑网络延迟 network latency,因为节点之间存在着数据交换和通信,这会影响到程序设计
分布式系统需要处理的两个问题:
容错
网络延迟
创建RDD
1、可以从已有的RDD创建
2、从SparkContext 创建
parallelize:convert a local scala collection to RDD
textFIle:read a text file from HDFS or lcoal file system and return a RDD
重命名 rename hdfs 文件
https://bigdata-etl.com/apache-spark-how-to-rename-or-delete-a-file-from-hdfs/
package com.bigdataetl import org.apache.hadoop.fs.{FileSystem, Path} import org.apache.spark.sql.SparkSession object Test extends App { val spark = SparkSession.builder // I set master to local[*], because I run it on my local computer. // I production mode master will be set rom s .master("local[*]") .appName("BigDataETL") .getOrCreate() // Create FileSystem object from Hadoop Configuration val fs = FileSystem.get(spark.sparkContext.hadoopConfiguration) // Base path where Spark will produce output file val basePath = "/bigtadata_etl/spark/output" val newFileName = "renamed_spark_output" // Change file name from Spark generic to new one fs.rename(new Path(s"$basePath/part-00000"), new Path(s"$basePath/$newFileName"))
判断hdfs 文件是否存在
val conf = sc.hadoopConfiguration val fs = org.apache.hadoop.fs.FileSystem.get(conf) val exists = fs.exists(new org.apache.hadoop.fs.Path("/path/on/hdfs/to/SUCCESS.txt"))
remove 删除 hdfs 文件
https://bigdata-etl.com/apache-spark-how-to-rename-or-delete-a-file-from-hdfs/
package com.bigdataetl import org.apache.hadoop.fs.{FileSystem, Path} import org.apache.spark.sql.SparkSession import scala.sys.process._ object SparkDeleteFile { val spark = SparkSession.builder // I set master to local[*], because I run it on my local computer. // I production mode master will be set rom s .master("local[*]") .appName("BigDataETL") .getOrCreate() // Create FileSystem object from Hadoop Configuration val fs = FileSystem.get(spark.sparkContext.hadoopConfiguration) // Delete directories recursively using FileSystem class fs.delete(new Path("/bigdata_etl/data"), true) // Delete using Scala DSL s"hdfs dfs -rm -r /bigdata_etl/data/" ! // Delete file fs.removeAcl(new Path("/bigdata_etl/data/file_to_delete.dat")) // Delete using Scala DSL s"hdfs dfs -rm /bigdata_etl/data/file_to_delete.dat" !
重命名本地文件 local file
import java.io.File import util.Try def mv(oldName: String, newName: String) = Try(new File(oldName).renameTo(new File(newName))).getOrElse(false)
判断本地文件是否存在 local file
scala> import java.nio.file.{Paths, Files} import java.nio.file.{Paths, Files} scala> Files.exists(Paths.get("/tmp")) res0: Boolean = true
intellij idea 远程调试 spark 程序
先执行
export SPARK_SUBMIT_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=4000
然后设置idea
Rub-> Edit Configuration -> Click on "+" left top cornor -> Remote -> set port and name
然后
spark-submit --class com.your.Appp --master local target/scala-2.11/your-app-assembly-1.0.jar
这个时候会出现
Listening for transport dt_socket at address: 4000
然后回到idea,下断点,点击 debug 开始调试
http://www.bigendiandata.com/2016-08-26-How-to-debug-remote-spark-jobs-with-IntelliJ/
local模式web ui 监控
idea debug 调试executor
1、提交的时候用下面的配置
--num-executors 1
--executor-cores 1
--conf spark.executor.extraJavaOptions=-agentlib:jdwp=transport=dt_socket,server=n,address=xxx.xxx.xxx.xxx:4000,suspend=n \
2、在idea里面设置 Run - Edit Configuration ,如果没有Remote就点击➕新建一个,然后将Host设置为自己电脑的IP,debug mode 设置成listen to remote JVM
https://stackoverflow.com/questions/29090745/debugging-spark-applications
spark操作多个集群