Spark与Mapreduce对比
MapReduce | Spark |
---|---|
数据存储结构:磁盘hdfs文件系统的split | 使用内存构建弹性分布式数据集RDD, 对数据进行运算和cache |
编程范式: Map + Reduce | DAG(有向无环图): Transformation + action |
计算中间数据落磁盘, io及序列化、 反序列化代价大 | 计算中间数据在内存中维护, 存取速度是磁盘的多个数量级 |
Task以进程的方式维护, 任务启动就有数秒 | Task以线程的方式维护, 对小数据集的读取能达到亚秒级的延迟 |
spark1.x源码编译
环境要求:
Maven:3.3.3+
Java:1.7+
Scala:2.10.4
将当前3.0.5的版本更换为3.3.3的版本,
Maven的安装:http://blog.csdn.net/haoyuexihuai/article/details/52985796
进入~/.m2目录将repository-1.6.1.tar.gz(maven包) 解压到当前目录:tar -zxvf repository-1.6.1.tar.gz ~/.m2
编译:
前提:安装好Maven、Scala、Java
- 解压
tar -zxvf /opt/software/spark-1.6.1.tar.gz -C /opt/tools/workspace
- 进入目录
cd /opt/tools/workspace/spark-1.6.1
修改:make-distribution.sh文件
- vim make-distribution.sh ## 修改130行开始的四个属性的值
- YARN
原始:
VERSION=1.6.0 SCALA_VERSION=2.10.4 SPARK_HADOOP_VERSION=2.5.0 / 2.5.0-cdh5.3.6 SPARK_HIVE=1
修改为:
- 修改pom文件:vim pom.xml
- 使用/2.10.5 查询 修改为2.10.4。文件中有两处需要修改
放依赖服务
tar -zxvf /opt/software/scala-2.10.4.tgz -C /opt/tools/workspace/spark-1.6.1/build/ tar -zxvf /opt/software/zinc-0.3.5.3.tgz -C /opt/tools/workspace/spark-1.6.1/build/
CDH编译源码
./make-distribution.sh --tgz \ -Phadoop-2.4 \ -Dhadoop.version=2.5.0-cdh5.3.6 \ -Pyarn \ -Phive -Phive-thriftserver
编译结束后在目录下生成一个:spark-1.6.0-bin-2.5.0-cdh5.3.6.tgz
Apache编译
./make-distribution.sh --tgz \ -Phadoop-2.4 \ -Dhadoop.version=2.5.0 \ -Pyarn \ -Phive -Phive-thriftserver
Spark安装部署:
Spark四种运行模式
- Local:本地运行模式,主要用于开发、测试
- Standalone:使用Spark自带的资源管理框架运行Spark程序,30%左右
- Yarn: 将spark应用程序运行在yarn(资源管理框架)上,绝大多数使用情况,60%左右
- Mesos:国内不常用
将spark-1.6.1.tar.gz解压,并添加到IDEA中,将文件夹的结构转换为代码的结构。
查询的默认快捷键:Ctrl + N ,Ctrl+F12
- 使用Ctrl+N 搜索:SparkContext
- Ctrl+F12搜索:textFile
Local模式安装
使用自己编译产生的tgz压缩包
步骤:
前提:安装Scala(2.10.4)和JDK(1.7.x+),之前已经安装
1. 解压到cdh-5.3.6目录
进入目录:cd /opt/cdh-5.3.6
解压tar:-zxvf /opt/tools/workspace/spark-1.6.1/spark-1.6.0-bin-2.5.0-cdh5.3.6.tgz
创建软连接:ln -s spark-1.6.0-bin-2.5.0-cdh5.3.6/ spark
2. 修改相关参数
cd /opt/cdh-5.3.6/spark/conf
mv spark-env.sh.template spark-env.sh
mv spark-defaults.conf.template spark-defaults.conf
修改:vim spark-env.sh
内容:
```
JAVA_HOME=/opt/modules/jdk1.7.0_67
SCALA_HOME=/opt/modules/scala
HADOOP_CONF_DIR=/opt/cdh-5.3.6/hadoop-2.5.0-cdh5.3.6/etc/hadoop
SPARK_LOCAL_IP=hadoop-senior01.ibeifeng.com
```
3. 启动HDFS
sbin/start-dfs.sh
4. 测试
进入启动目录:cd /opt/cdh-5.3.6/spark/bin
删除所有以cmd(Windows下使用)结尾的文件:rm *cmd
启动:./spark-shell
启动后创建了一个metastore_db
17/02/14 10:37:23 INFO ui.SparkUI: Started SparkUI at http://192.168.66.141:4040
17/02/14 10:37:49 INFO repl.SparkILoop: Created sql context (with Hive support)..
SQL context available as sqlContext.
创建路径:bin/hdfs dfs -mkdir -p /beifeng/spark/
上传文件:bin/hdfs dfs -put /opt/cdh-5.3.6/spark/README.md /beifeng/spark/
Standalone结构配置
Standalone模式是Spark自身管理资源的一个模式,类似Yarn
Yarn的结构:
- ResourceManager: 负责集群资源的管理
- NodeManager:负责当前机器的资源管理
CPU&内存
SparkStandalone的结构:Master: 负责集群资源管理
- Worker: 负责当前机器的资源管理
- CPU&内存
配置安装:
- 前提:基于Local模式下的进行修改安装
- 前提2:所有机器以及完成SSH免密码登录
安装步骤:
修改spark-env.sh:vim spark-env.sh
SPARK_MASTER_IP=hadoop-senior01.ibeifeng.com SPARK_MASTER_PORT=7070 SPARK_MASTER_WEBUI_PORT=8080 SPARK_WORKER_CORES=3 ## 一个work分配的cpu数量 SPARK_WORKER_MEMORY=3g ## 一个work分配的内存数量 SPARK_WORKER_PORT=7071 SPARK_WORKER_WEBUI_PORT=8081 SPARK_WORKER_INSTANCES=2 ## 一台机器允许同时存在的work的数量
修改slaves.template,给定work节点的hostname
mv slaves.template slaves vim slaves ## 一行一个hostname
启动服务:cd /opt/cdh-5.3.6/spark
sbin/start-all.sh
- 日志位于:/opt/cdh-5.3.6/spark/logs文件夹中
访问页面:http://hadoop-senior01.ibeifeng.com:8080/
测试wordcount程序
bin/spark-shell --master spark://hadoop-senior01.ibeifeng.com:7070
sc.textFile("/input/wc.input"). filter(_.length > 0). flatMap(_.split(" ").map((_,1))). reduceByKey(_ + _). top(3)(ord = new scala.math.Ordering[(String,Int)](){ // 自定义数据排序类 override def compare(x: (String,Int), y: (String,Int)) = { val tmp = x._2.compare(y._2) if (tmp != 0) tmp else -x._1.compare(y._1) } })
Spark Shell测试
// 前提:spark集成hdfs
// textFile默认是HDFS上的路径,除非给定特定的schema(HDFS://)
val textFile = sc.textFile(“/beifeng/spark/README.md”)
/*
textFile: org.apache.spark.rdd.RDD[String] = /beifeng/spark/README.md MapPartitionsRDD[1] at textFile at :27 */
textFile.count()
/*
res2: Long = 95
*/
textFile.first()
/*
res3: String = # Apache Spark
*/
过滤
val linesWithSpark = textFile.filter(line => line.contains(“Spark”))
linesWithSpark.take(3)
/*
res4: Array[String] = Array(# Apache Spark, Spark is a fast and general cluster computing system for Big Data. It provides, rich set of higher-level tools including Spark SQL for SQL and DataFrames,)
*/
linesWithSpark.count()
/*
res5: Long = 17
*/
SparkCore开发
步骤:
1. 数据加载
val rdd = sc.textFilexxx
2. 数据处理
val rdd2 = rdd.xxxx
3. 结果输出
rdd2.xxx
SparkCore WordCount
MapReduce:
MapTask:
数据过滤、数据转换(转换为word和次数<word,1>)
Shuffle:
数据排序 + 数据分组
ReduceTask:
数据的聚合
// Step1: 读取HDFS上的文件/input/wc.input
val rdd = sc.textFile("/input/wc.input")
// Step2: 数据过滤
/*
Return a new RDD containing only the elements that satisfy a predicate.
def filter(f: T => Boolean): RDD[T]
T: 此时是String
最终返回的RDD中的数据是执行f函数后返回结果为true的数据
*/
val filterRDD = rdd.filter(len => len.length > 0)
// Step3: 数据转换
/*
def map[U: ClassTag](f: T => U): RDD[U]
f:从T到U类型的一个数据转换函数
map的最终返回的RDD中的数据类型是f函数返回的数据类型
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]
f: 从T到集合类型的一个数据转换函数,集合中的数据类型是U
flatMap的最终返回的RDD中的数据类型是f函数返回的集合中的具体的数据类型,比如U
flatMap和map函数的区别:
flatMap在map函数的基础上进行一次数据扁平化的操作
数据扁平化:将集合中的数据进行一个提升,删除集合
*/
val mapperRDD = filterRDD.flatMap(line => line.split(" ").map(word => (word,1)))
// Step4: 数据的分组,相同的word分配到一组
/*
def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
f: 是一个从T到K进行转换的一个函数
返回RDD的内部数据类型是一个二元组,第一个元素是K类型的数据,第二个元素是是一个T元素数据的迭代器
原理:将K相同的数据放到一起
*/
val groupRDD = mapperRDD.groupBy(tuple => tuple._1)
查看一下数据格式:groupRDD.foreach(println)
// Step5 数据聚合 => 数据的一个累计计算/就是一个转换
val wordCountRDD = groupRDD.map(tuple => {
val word = tuple._1 // 单词
// val sum = tuple._2.toList.map(tuple => tuple._2).sum
val sum = tuple._2.toList.foldLeft(0)((a,b) => a+b._2)
// 返回结果
(word,sum)
})
// Step6数据结果输出
wordCountRDD.foreach(println)
wordCountRDD.foreachPartition(iter => iter.foreach(println)) // 一个分区一个分区的进行处理
wordCountRDD.saveAsTextFile("/beifeng/wordcount/output02")
// wordcount简化 ==> 链式编程
sc.textFile("/input/wc.input").
// 数据过滤
filter(_.length > 0).
// 数据的转换
flatMap(_.split(" ").map((_,1))).
// 分组
groupByKey().
// 统计计数
map(tuple => (tuple._1, tuple._2.toList.sum)).
// 结果输出
saveAsTextFile("/beifeng/wordcount/output03")
// Wordcount最优的一种
sc.textFile("/input/wc.input").
// 数据过滤
filter(_.length > 0).
// 数据的转换
flatMap(_.split(" ").map((_,1))).
// 统计计数
reduceByKey(_ + _).
// 结果输出
saveAsTextFile("/beifeng/wordcount/output04")
/*
选择使用reduceByKey函数的主要原因是:reduceByKey中存在combiner
不使用groupByKey的主要原因:在大规模的数据下,数据分布不均匀的情况下,可能导致OOM
As currently implemented, groupByKey must be able to hold all the key-value pairs for any key in memory. If a key has too many values, it can result in an [[OutOfMemoryError]].
*/
sc.textFile("/input/wc.input").
filter(_.length > 0).
flatMap(_.split(" ").map((_,1))).
reduceByKey(_ + _).
collect()
// Array[(String, Int)] = Array((mapreduce,2), (hello,1), (yarn,1), (spark,1), (hadoop,4), (hdfs,1))
// 排序:最终结果按照count数量从大到小排序
sc.textFile("/input/wc.input").
filter(_.length > 0).
flatMap(_.split(" ").map((_,1))).
reduceByKey(_ + _).
sortBy(tuple => tuple._2,ascending = false).
collect()
// Array[(String, Int)] = Array((hadoop,4), (mapreduce,2), (hello,1), (yarn,1), (spark,1), (hdfs,1))
sc.textFile("/input/wc.input").
filter(_.length > 0).
flatMap(_.split(" ").map((_,1))).
reduceByKey(_ + _).
map(tuple => (tuple._2,tuple._1)).
sortByKey(ascending = false).
map(tuple => (tuple._2,tuple._1)).
collect()
// res33: Array[(String, Int)] = Array((hadoop,3), (hbase,3), (hive,2), (sqoop,2), (spark,1), (oozie,1))
// TOP K
// 作业:MapReduce TOP K的实现伪代码
sc.textFile("/input/wc.input").
filter(_.length > 0).
flatMap(_.split(" ").map((_,1))).
reduceByKey(_ + _).
map(tuple => (tuple._2,tuple._1)).
sortByKey(ascending = false).
map(tuple => (tuple._2,tuple._1)).
take(3)
sc.textFile("/input/wc.input").
filter(_.length > 0).
flatMap(_.split(" ").map((_,1))).
reduceByKey(_ + _).
top(3)(ord = new scala.math.Ordering[(String,Int)](){
// 自定义数据排序类
override def compare(x: (String,Int), y: (String,Int)) = {
val tmp = x._2.compare(y._2)
if (tmp != 0) tmp
else -x._1.compare(y._1)
}
})