在上一篇文章中,讲了Spark的简单应用开发,在构建数据源的时候,返回了一个RDD对象,所有对数据的操作,都是在这个对象中进行操作,RDD对象是Spark中至为核心的组件,这篇文章就一起来谈谈Spark RDD (resilient distributed dataset)
什么是RDD?
RDD( resilient distributed dataset ) 弹性分布式数据集;RDD代表是一个不可变的、可分区的、支持并行计算的元素集合(类似于Scala中的不可变集合),RDD可以通过HDFS、Scala集合、RDD转换、外部的数据集(支持InputFormat)获得;并且我们可以通知Spark将RDD持久化在内存中,可以非常高效的重复利用或者在某些计算节点故障时自动数据恢复;
如何创建RDD
Scala 集合创建
数组中数字进行求和计算,两种方法
- parallelize
- makeRDD (查看源码最终调用的还是
parallelize
)
package com.netzhuo
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object RDDByCollectionTest extends App {
// 设置运行核心为本机CPU数量
val conf: SparkConf = new SparkConf().setAppName("collection").setMaster("local[*]")
val sc = new SparkContext(conf)
val range: Range = 1 to 100
// m1(sc)
// m2(sc)
sc.stop()
/**
* 集合启动任务二
* @param sc
*/
def m2(sc: SparkContext): Unit ={
val rdd = sc.makeRDD(range,3)
val sum = rdd.reduce((t1:Int,t2:Int) => t1 + t2)
println(s"rdd.分区数量 = ${rdd.getNumPartitions}")
println(sum)
}
/**
* 集合启动任务一
* @param sc
*/
def m1(sc: SparkContext): Unit ={
val rdd: RDD[Int] = sc.parallelize(range,2)
val sum = rdd.reduce((t1:Int,t2:Int) => t1 + t2)
println(s"rdd.分区数量 = ${rdd.getNumPartitions}")
println(sum)
}
}
通过外部数据集构建
文件系统
Local(本地文件系统)
通过本地文件系统创建RDD 两种方法
- wholeTextFiles
- textFile
package com.netzhuo
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object RDDByLocalFileTest extends App {
val conf: SparkConf = new SparkConf().setAppName("localFile").setMaster("local[*]")
val sc = new SparkContext(conf)
// m1(sc)
m2(sc)
sc.stop()
/**
* 方法二
* 通过本地文件目录创建RDD
* 输出目录下的所有文件中的内容
* @param sc
*/
def m2(sc: SparkContext): Unit ={
// k:v k= path v= value
val rdd:RDD[(String, String)] = sc.wholeTextFiles("file:///G:\\testData",1)
// rdd.foreach(println)
rdd
.map(t2 => t2._2) // 获取出来每一行数据
.flatMap(_.split("\\n")) // 对数据按行进行切割
.flatMap(_.split("\\s"))
.map((_,1L))
.groupByKey()
.map(t2=>(t2._1,t2._2.size))
.sortBy(t2=>t2._1) // 分区数量为1的时候进行排序
.foreach(println(_))
}
/**
* 方法一
* 通过本地文件目录创建RDD
* 输出目录下的所有文件中的内容
* @param sc
*/
def m1(sc: SparkContext): Unit ={
val rdd: RDD[String] = sc.textFile("file:///G:\\testData",5)
rdd.foreach(println)
}
}
HDFS
使用HDFS文件系统创建RDD两种方法和通过本地文件系统创建方法一样,将文件路径修改为HDFS中的文件
package com.netzhuo
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object RDDByHDFSFileTest extends App {
val conf: SparkConf = new SparkConf().setAppName("localFile").setMaster("local[*]")
val sc = new SparkContext(conf)
m1(sc)
// m2(sc)
sc.stop()
/**
* 方法二
* 通过HDFS目录创建RDD
* 输出目录下的所有文件中的内容
* @param sc
*/
def m2(sc: SparkContext): Unit ={
// k:v k= path v= value
val rdd:RDD[(String, String)] = sc.wholeTextFiles("hdfs://SparkOnStandalone:9000/data.txt",1)
// rdd.foreach(println)
rdd
.map(t2 => t2._2) // 获取出来每一行数据
.flatMap(_.split("\\n")) // 对数据按行进行切割
.flatMap(_.split("\\s"))
.map((_,1L))
.groupByKey()
.map(t2=>(t2._1,t2._2.size))
.sortBy(t2=>t2._1) // 分区数量为1的时候进行排序
.foreach(println(_))
}
/**
* 方法一
* 通过HDFS目录创建RDD
* 输出目录下的所有文件中的内容
* @param sc
*/
def m1(sc: SparkContext): Unit ={
val rdd: RDD[String] = sc.textFile("hdfs://SparkOnStandalone:9000/data.txt",5)
rdd.foreach(println)
}
}
关系型数据库
通过MySQL构建RDD
导入驱动jar包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
使用
JdbcRDD
创建需要指定上下边界(范围查询),不方便
使用newAPIHadoopRDD
创建需要使用hadoopConfiguration
对象进行设置连接属性
package com.netzhuo
import java.sql.DriverManager
import org.apache.hadoop.io.LongWritable
import org.apache.hadoop.mapreduce.lib.db.{DBConfiguration, DBInputFormat}
import org.apache.spark.rdd.{JdbcRDD, RDD}
import org.apache.spark.{SparkConf, SparkContext}
/**
* 外部数据源mysql创建RDD
*/
object RDDByMysqlTest extends App {
val conf: SparkConf = new SparkConf().setAppName("localFile").setMaster("local[*]")
val sc = new SparkContext(conf)
// m1(sc)
m2(sc)
sc.stop()
/**
* 方法二
*
* @param sc
*/
def m2(sc: SparkContext): Unit = {
val conf = sc.hadoopConfiguration
conf.set(DBConfiguration.DRIVER_CLASS_PROPERTY, "com.mysql.jdbc.Driver")
conf.set(DBConfiguration.URL_PROPERTY, "jdbc:mysql://172.16.5.199:3306/test")
conf.set(DBConfiguration.USERNAME_PROPERTY, "root")
conf.set(DBConfiguration.PASSWORD_PROPERTY, "root")
conf.set(DBConfiguration.INPUT_QUERY, "select * from t_user")
// 查询数据库表的记录数,用以计算分区
conf.set(DBConfiguration.INPUT_COUNT_QUERY, "select count(*) from t_user")
conf.set(DBConfiguration.INPUT_CLASS_PROPERTY, "com.netzhuo.User")
val rdd = sc.newAPIHadoopRDD(conf, classOf[DBInputFormat[User]], classOf[LongWritable], classOf[User])
println(s"rdd.getNumPartitions = ${rdd.getNumPartitions}")
rdd
.foreach(t2 => {
println("k=" + t2._1 + " | v=" + t2._2)
})
}
/**
* 方法一
* 使用Mysql 库中的数据创建RDD
* 通过JDBCRDD构建 有比较大的局限性,原因是SQL语句中必须定义上下边界,否则无法使用
* @param sc
*/
def m1(sc: SparkContext): Unit ={
val rdd = new JdbcRDD(
sc,
() => {
Class.forName("com.mysql.jdbc.Driver")
val connection = DriverManager.getConnection("jdbc:mysql://172.16.5.199:3306/test", "root", "root")
connection
},
"select * from t_user where id >= ? and id <= ?",
1, // 下边界
4, // 上边界
1, // 分区数量
rs => {
val id = rs.getInt("id")
val name = rs.getString("name")
val sex = rs.getBoolean("sex")
(id, name, sex)
}
)
rdd.foreach(t3 => println(t3._1,t3._2,t3._3))
}
}
User 实体类,需要实现DB接口
package com.netzhuo
import java.sql.{PreparedStatement, ResultSet}
import org.apache.hadoop.mapreduce.lib.db.DBWritable
class User extends DBWritable{
var id:Int = _
var name: String = _
var sex: Boolean = _
override def toString = s"User($id, $name, $sex)"
override def write(preparedStatement: PreparedStatement): Unit = {
preparedStatement.setInt(1,id)
preparedStatement.setString(2,name)
preparedStatement.setBoolean(3, sex)
}
override def readFields(resultSet: ResultSet): Unit = {
id = resultSet.getInt(1)
name = resultSet.getString(2)
sex = resultSet.getBoolean(3)
}
}
运行
- 本地执行
- 打包jar包,远程提交
spark应用在计算时使用到第三方依赖jar包,在spark集群运行时如果没有提供此jar包,则会发生
java.lang.ClassNotFoundException
第一种方法:
将第三方依赖jar包,提前准备到spark集群的每一个计算节点
[root@SparkOnStandalone spark-2.4.4]# mkdir 3rdLib
[root@SparkOnStandalone spark-2.4.4]# mv ~/mysql-connector-java-5.1.47.jar /usr/spark-2.4.4/3rdLib/
# 修改spark的配置文件 从而能够加载第三方的驱动jar包
[root@SparkOnStandalone spark-2.4.4]# vi conf/spark-defaults.conf
# 配置文件声明jar包扩展目录
spark.executor.extraClassPath=/usr/spark-2.4.4/3rdLib/*
spark.driver.extraClassPath=/usr/spark-2.4.4/3rdLib/*
# 重启spark集群
[root@SparkOnStandalone spark-2.4.4]# sbin/stop-all.sh
SparkOnStandalone: stopping org.apache.spark.deploy.worker.Worker
SparkOnStandalone: stopping org.apache.spark.deploy.worker.Worker
stopping org.apache.spark.deploy.master.Master
[root@SparkOnStandalone spark-2.4.4]# sbin/start-all.sh
第二种方案【推荐】:
将第三方远程依赖,打包到计算应用中;
# 1. 在maven配置文件中添加一个打包插件
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<!--这里要替换成jar包main方法所在类 -->
<mainClass>com.netzhuo.RDDByHBaseTest</mainClass>
</manifest>
<manifestEntries>
<Class-Path>.</Class-Path>
</manifestEntries>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- 指定在打包节点执行jar包合并操作 -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
打包结束后会获得两个jar包:
xxx.jar
和xxx-jar-with-dependencies.jar
- 第一个jar包是不包含第三方依赖的普通jar包
- 第二个jar包是包含第三方完整依赖的特殊jar包
包含完整依赖的特殊jar包上传到linux中提交运行
bin/spark-submit --master spark://SparkOnStandalone:7077 --total-executor-cores 2 --class com.netzhuo.RDDByHBaseTest /tmp/SparkDay02-1.0-SNAPSHOT-jar-with-dependencies.jar
第三种方法:
提交spark应用是,添加参数,在线下载需要的第三方依赖;
注意:不适用于MySQL
# 查看帮助
./bin/spark-shell --help
--packages 声明需要依赖的第三方依赖坐标 多个依赖用","分割 如: mysql:mysql-connector-java:5.1.47,....
--repositories 第三方maven镜像仓库地址
[root@SparkOnStandalone spark-2.4.4]# bin/spark-submit --master spark://SparkOnStandalone:7077 --class com.baizhi.datasource.RDDCreatedByDB --total-executor-cores 2 /root/spark-day2-1.0-SNAPSHOT.jar --packages mysql:mysql-connector-java:5.1.47
通过HBase创建
导入HBase第三方依赖
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.4.10</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.4.10</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.5.0</version>
</dependency>
启动HBase服务,准备外部的数据集
启动HDFS,zookeeper,Hbase
jps
确认环境正常
3506 HRegionServer
3669 Jps
2742 QuorumPeerMain
2183 DataNode
2408 SecondaryNameNode
3353 HMaster
2059 NameNode
scan 'netzhuo1':t_user
查看 t_user 中的数据
开发应用
本地运行或者打包完整jar包远程运行
package com.netzhuo
import java.sql.DriverManager
import org.apache.hadoop.hbase.HConstants
import org.apache.hadoop.hbase.client.Result
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.hadoop.hbase.mapreduce.TableInputFormat
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* HBase 创建RDD
*/
object RDDByHBaseTest extends App {
val conf: SparkConf = new SparkConf().setAppName("localFile").setMaster("local[*]")
val sc = new SparkContext(conf)
val configuration = sc.hadoopConfiguration
configuration.set(HConstants.ZOOKEEPER_QUORUM,"HadoopNode00")
configuration.set(HConstants.ZOOKEEPER_CLIENT_PORT,"2181")
// HBase 字段名称
configuration.set(TableInputFormat.INPUT_TABLE,"netzhuo1:t_user")
// 查询字段列表
configuration.set(TableInputFormat.SCAN_COLUMNS,"cf1:name cf1:age")
val rdd: RDD[(ImmutableBytesWritable, Result)] = sc.newAPIHadoopRDD(
configuration,
classOf[TableInputFormat],
classOf[ImmutableBytesWritable],
classOf[Result])
println(s"rdd.getNumPartitions = ${rdd.getNumPartitions}")
rdd.foreach(t2 =>{
val rowkey = Bytes.toString(t2._1.get())
val name = Bytes.toString(t2._2.getValue(Bytes.toBytes("cf1"),Bytes.toBytes("name")))
val age = Bytes.toString(t2._2.getValue(Bytes.toBytes("cf1"),Bytes.toBytes("age")))
println(rowkey,name,age)
})
sc.stop()
}
SparkOnYarn
Spark 的特性
Runs Everywhere
,可以在Yarn环境下运行,在这里尝试部署Spark到Yarn环境下
配置系统环境
-
Centos 7
- 关闭防火墙
- 配置网络
- 配置IP地址映射
- 配置SSH免密登录
-
Java 1.8
-
安装JDK 1.8以上
-
安装Hadoop
-
安装Spark
yarn 环境下提交命令
./bin/spark-submit --master yarn --executor-cores 2 --num-executors 2 --class com.netzhuo.RDDByHBaseTest /tmp/SparkDay02-1.0-SNAPSHOT-jar-with-dependencies.jar
yarn 环境下shell 命令
bin/spark-shell --master yarn --executor-cores 2 --num-executors 2