当我们谈spark的时候我们在关注什么

虽然能够用scala磕磕绊绊写spark了,但跟大数据相关的一些繁琐的名词及其材料仍让人看的云里雾里的,时至今日中午仍旧在这部分晕晕乎乎,下午突然想通了一个点,再看了下以前浏览的资料,顿有茅塞顿开之感。为此review了林子雨老师的教程,并记录下一些自认为重要的点。

茅塞顿开的点

想通的点是大数据面临的问题主要有两个:如何存储如何使用。基于这个基础理念,再看了下以下材料进行验证:

  1. 林子雨老师教程中的第一章。第一遍看林老师教程的时候非常懵逼,甚至看不下去,这次再看颇有种跟林老师一拍即合的感觉,ppt做的真是行云流水、字字珠玉,令人拍案叫绝,里面的图标表也是非常令人赏心悦目了。
  2. hive和sql的区别,言而总之Map Reduce基础的写法对初学者不友好,于是给会sql的人提供了hive接口来访问数据. Hive: SQL-on-Hadoop
  3. MapReduce框架将数据处理完后写入到Disk,即使只处理到中间步骤,也要借助Disk进行;Spark也用了MapReduce框架中的MapReduce方式,但是中间过程存储在memory,显然,处理速度非常高了。
  4. RDD本质是个只读的分区记录集合,因此不能直接修改数据,只能通过执行确定的转换操作(如map、join和group by)创建得到新的RDD
  5. RDD VS DataFrame:1)RDD是分布式的 Java对象的集合,但是,对象内部结构对于RDD而言却是不可知的;2)DataFrame是一种以RDD为基础的分布式数据集,提供了详细的结构信息
    在这里插入图片描述
  6. 了解RDD的宽/窄依赖可以用来提高计算效率
  7. Spark为包含键值对类型的RDD提供了一些专有的操作,这些RDD被成为pair RDD
  8. RDD和pair RDD的操作都包括**转换(Transformation)和行动(Action)**操作。
  9. RDD典型的执行过程是:创建->转换->转换…->转换->动作,从执行过程就能看到RDD主要操作是转换。
  10. RDD的依赖关系可以构建DAG图,DAG和惰性机制配合使用。
  11. RDD的惰性机制是指:RDD整个转换过程只记录了转换轨迹,并不会发生真正的计算,只有遇到行动操作时,才会发生真正的计算。
  12. RDD的持久化是说:RDD采用惰性求值的机制,每次遇到行动操作,都会从头开始执行计算。每次调用行动操作,都会触发一次从头开始的计算。这对于迭代计算而言,代价是很大的,迭代计算经常需要多次重复使用同一组数据。可以使用persist()方法对一个RDD标记为持久化,但是“标记为持久化”并不会马上计算生成RDD并把它持久化,而是要等到遇到第一个行动操作触发真正计算以后,才会把计算结果进行持久化。持久化后的RDD将会被保留在计算节点的内存中被后面的行动操作重复使用。
  13. 持久化级别可以设置:
    • persist(MEMORY_ONLY):表示将RDD作为反序列化的对象存储于JVM中,如果内存不足,就要按照LRU原则替换缓存中的内容
    • persist(MEMORY_AND_DISK)表示将RDD作为反序列化的对象存储在JVM中,如果内存不足,超出的分区将会被存放在硬盘上
    • 一般而言,使用.cache()方法时,会调用persist(MEMORY_ONLY)
    • 可以使用unpersist()方法手动地把持久化的RDD从缓存中移除
  14. 分区?还是有点不是很清楚,所以不能做通俗解释,之后要有茅塞顿开时更新
  15. 共享变量有广播变量累加变量两种。
  16. Hadoop、HDFS、Hive、Hbase之间的关系

在大数据架构中,Hive和HBase是协作关系,数据流一般如下:

  1. 通过ETL工具将数据源抽取到HDFS存储;
  2. 通过Hive清洗、处理和计算原始数据;
  3. HIve清洗处理后的结果,如果是面向海量数据随机查询场景的可存入Hbase
  4. 数据应用从HBase查询数据;
  1. 行存储和列存储

OLTP(Online Transaction Processing,联机事务处理)一般查询较多,计算较少,适合列存储。比如hbase
OLAP(Online analytical processing,联机分析处理)一般计算分析较多,适合行存储。比如sql

一些操作

1. RDD

1.1 创建

可以从文件(本地系统、HDFS等的地址)或者 数组 创建

  1. 从文件
    // 1.1 从本地文件
    val lines = sc.textFile("file:///usr/local/spark/mycode/rdd/word.txt")
    // 1.2 从HDFS文件
    val lines = sc.textFile("hdfs://localhost:9000/user/hadoop/word.txt")
    
  2. 从数组:可以调用SparkContext的parallelize方法,在Driver中一个已经存在的集合(数组)上创建。
    val array = Array(1,2,3,4,5)
    val rdd = sc.parallelize(array)
    val list = List(1,2,3,4,5)
    val rdd = sc.parallelize(list)
    

1.2 常见操作

  1. 转换操作:mapflatMapfiltergroupByKeyreduceByKey
    • flatMap:与map()相似,但每个输入元素都可以映射到0或多个输出结果
    • groupByKey():应用于(K,V)键值对的数据集时,返回一个新的(K, Iterable)形式的数据
    • reduceByKey(func):应用于(K,V)键值对的数据集时,返回一个新的(K, V)形式的数据集,其中的每个值是将每个key传递到函数func中进行聚合
  2. RDD常见的行动操作:reducecountcollectfirsttakeforeach
    • reduce:通过函数func(输入两个参数并返回一个值)聚合数据集中的元素

1.3 实例

  1. 找出文本文件中单行文本所包含的单词数量的最大值
    val lines = sc.textFile("file:///usr/local/spark/mycode/rdd/word.txt")
    lines.map(line => line.split(" ").size).reduce((a,b) => if (a>b) a else b)
    

2. Pair RDD

2.1 创建

// 从文件
val lines = sc.textFile("file:///usr/local/spark/mycode/pairrdd/word.txt")
val pairRDD = lines.flatMap(line => line.split(" ")).map(word => (word,1))

// 从数组
val list = List("Hadoop","Spark","Hive","Spark")
val rdd = sc.parallelize(list)
val pairRDD = rdd.map(word => (word,1))

2.2 常见操作

  1. 转换操作:
    val words = Array("one", "two", "two", "three", "three", "three") 
    val wordPairsRDD = sc.parallelize(words).map(word => (word, 1))
    
    wordPairsRDD.collect() 
    // Array[(String, Int)] = Array((one,1), (two,1), (two,1), (three,1), (three,1), (three,1))
    wordPairsRDD.take(2).foreach(println) // 但是foreach只是便利,没有返回
    
    1. reduceByKey(fuc): 使用func函数合并具有相同键的值
    wordPairsRDD.reduceByKey(_ + _)
    // org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[2] at reduceByKey at <console>:25
    wordPairsRDD.reduceByKey(_ + _).collect()
    // Array[(String, Int)] = Array((one,1), (two,2), (three,3))
    
    1. groupByKey(): 对具有相同键的值进行分组
    var wordCountsWithGroup = wordPairsRDD.groupByKey()
    // org.apache.spark.rdd.RDD[(String, Iterable[Int])] = ShuffledRDD[5] at groupByKey at <console>:26
    wordCountsWithGroup.collect().foreach(println)
    //(one,CompactBuffer(1))
    //(two,CompactBuffer(1, 1))
    //(three,CompactBuffer(1, 1, 1))
    wordCountsWithGroup.map(t => (t._1, t._2.sum)).collect()
    //  Array[(String, Int)] = Array((one,1), (two,2), (three,3))
    
    1. keys / values
    wordPairsRDD.keys.collect
    // Array[String] = Array(one, two, two, three, three, three)
    
    1. sortByKey()/sortBy()
    d1.reduceByKey(_+_).sortByKey(false).collect
    // Array[(String, Int)] = Array((g,21), (f,29), (e,17), (d,9), (c,27), (b,38), (a,42))
    d1.reduceByKey(_+_).sortByKey().collect()
    // Array[(String, Int)] = Array((a,42), (b,38), (c,27), (d,9), (e,17), (f,29), (g,21))
    d1.reduceByKey(_+_).sortBy(_._2,false).collect 
    // Array[(String, Int)] = Array((a,42), (b,38), (f,29), (c,27), (g,21), (e,17), (d,9))
    
    1. mapValues(func)
    wordPairsRDD.mapValues(x => x+1).collect()
    // Array[(String, Int)] = Array((one,2), (two,2), (two,2), (three,2), (three,2), (three,2))
    
    1. join: 对于给定的两个输入数据集(K,V1)和(K,V2),只有在两个数据集中都存在的key才会被输出,最终得到一个(K,(V1,V2))类型的数据集。
    2. combineByKey

2.3 实例

3. 数据读写

3.1 本地文件系统 数据读写

val textFile = sc.textFile("file:///usr/local/spark/mycode/wordcount/word.txt")
textFile.first()

val textFile = sc.textFile("file:///usr/local/spark/mycode/wordcount/word.txt")
textFile.saveAsTextFile("file:///usr/local/spark/mycode/wordcount/writeback.txt")
// ls /usr/local/spark/mycode/wordcount/writeback.txt/
// part-0000000 _SUCCESS

// 再次加载数据
val textFile = sc.textFile("file:///usr/local/spark/mycode/wordcount/writeback.txt")

3.2 分布式文件系统HDFS 数据读写

val textFile = sc.textFile("hdfs://localhost:9000/user/hadoop/word.txt"
textFile.saveAsTextFile("writeback.txt")

3.3 JSON文件 数据读写

json文件存放了如下数据

{"name":"Michael"}
{"name":"Andy", "age":30}
{"name":"Justin", "age":19}
val jsonStr = sc.textFile("file:///usr/local/spark/examples/src/main/resources/people.json")
jsonStr.foreach(println)
// {"name":"Michael"}
// {"name":"Andy", "age":30}
// {"name":"Justin", "age":19}

Scala中有一个自带的JSON库 scala.util.parsing.json.JSON,可以实现对JSON数据的解析

val result = jsonStrs.map(s => JSON.parseFull(s))
result.foreach( {r => r match {
	case Some(map: Map[String, Any]) => println(map)
	case None => println("Parsing failed")
	case other => println("Unknown data structure: " + other)
	} 
}

3.4 csv文件读取

val df = sparkSession.read.option("header","true").csv("src/main/resources/sales.csv")

4. 其他

4.1 打印元素

rdd.map(println)
rdd.foreach(println)
// 如果量太大
rdd.take(10).map(println)
rdd.take(10).foreach(println)

5. DataFrame

5.1 创建

参考了

  1. toDF 函数
    import spark.implicits._
    // 1. seq to df
    val df = Seq(
      (1, "First Value", java.sql.Date.valueOf("2010-01-01")),
      (2, "Second Value", java.sql.Date.valueOf("2010-02-01"))
    ).toDF("int_column", "string_column", "date_column")
    
    // 2. list to df
    val_df = List((1, "tank1", 25),(2, "tank2", 26),(3, "tank3", 29)).toDF("id", "name", "age")
    
    // 3. rdd to df
    val df = rdd.map(row => (row._1, row._2)).toDF('a','b')
    df.show()
    
  2. createDataFrame函数
    // from list
    val df = spark.createDataFrame(List(("Scala", 35), ("Python", 30), ("R", 15), ("Java", 20)))
    
  3. sql方式
    val sql_str = "select title,query0 from title_query_table limit 10"
    val title_query_df = sparkSession.sql(sql_str)
    
  4. 文件
    // 1. json文件
    val df = sparkSession.read.json("examples/src/main/resources/people.json")
    df.show()
    
    val dfJson = sparkSession.read.format("json").load("/Users/shirukai/Desktop/HollySys/Repository/sparkLearn/data/student.json")
    dfJson.show()
    
    // 2. csv文件
    val df = sparkSession.read
            .format("com.databricks.spark.csv")
            .option("header", "true") //reading the headers
            .option("mode", "DROPMALFORMED")
            .load("csv/file/path"); //.csv("csv/file/path") //spark 2.0 api
    df.show()
    
    val dfCsv = spark.read.format("csv").option("header", true).load("/Users/shirukai/Desktop/HollySys/Repository/sparkLearn/data/students.csv")
    dfCsv.show()
    

5.2 常见操作

//  rename the columns (如果没给名字,默认是_1,_2...)
val lpDF = df.withColumnRenamed("_1", "language").withColumnRenamed("_2", "percent")
//order the DataFrame in descending order of percentage
lpDF.orderBy(desc("percent")).show(false)

6. sparkSession

6.0 SparkConf、SparkSession、SparkContext、HiveContext、SQLContext

看上去令人晕晕乎乎,实际上并不难理解。参考123进行总结如下

对上面进行解释
在SparkSession提出之前,创建一个Application对应的上下文:

//set up the spark configuration and create contexts
val sparkConf = new SparkConf().setAppName("SparkSessionZipsExample").setMaster("local")
// your handle to SparkContext to access other context like SQLContext
val sc = new SparkContext(sparkConf).set("spark.some.config.option", "some-value")
val sqlContext = new org.apache.spark.sql.SQLContext(sc) //需要在sparkcontext基础上创建sqlcontext

提出之后,可以用builder创建。以下代码跟上述等价:

val spark = SparkSession
	.builder()
	.appName("hirw-test")
	.getOrCreate()

如果要支持Hive,添加enableHiveSupport()

val spark = SparkSession
	.builder()
	.appName("hirw-hive-test")
	.enableHiveSupport()
	.getOrCreate()

6.1 创建

如6.0的最后面两块代码所写

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值