初次尝试用 Spark+scala 完成项目的重构,由于两者之前都没接触过,所以边学边用的过程大多艰难。首先面临的是如何快速上手,然后是代码调优、性能调优。本章主要记录自己在项目中遇到的问题以及解决方式,下篇会尝试调优方法。末尾会分享自己的学习资料,也供大多菜鸟第一次使用作为参考。由于自己项目中大量使用spark sql,所以下面的经验大多是和spark sql有关。同样下面也列出作为菜鸟在学习过程中的困惑以及踩的坑,还请大牛勿笑 ~_~ 如果有更好的方式解决,欢迎留言,一起学习。

1、常用场景

(1)场景一:rdd读取指定行分隔符的数据,不以每行为单位

例1:配置文件中有n个sql语句,每个sql以分号----分隔。你需要读取sql,分别从hdfs中拉取数据。可能会采取:

//conf_sql_map_file 是sql配置文件
val sql_rdd = sc.textFile(conf_sql_map_file)
var sqls = sql_rdd.collect().mkString(" ").split("----")

分析:由于rdd以每行为单位,自动去掉结尾的 换行符。但sql配置文件需要以指定分隔符分隔,而不是每行。所以使用 mkString(" ") 将读取的每行数据以空格分隔,整合为一个长字符串,最后以分隔符分隔。

但如果 sql 语句中有使用 with 之类的关键词时,上面那种方式读取配置文件会因为格式问题会出错,with语句需要和 select 语句空行分隔,为保险起见,以 “\n” 分隔,还原配置文件的原始格式。

var sqls = sql_rdd.collect().mkString("\n").split("----")

(2)场景二:读取文件,以 key-value 形式存储。

例2:文件file1内容如下

key1,value1

key2,value2

var file_rdd = sc.textFile(file1).map(e=> (e.split(',')(0),e.split(',')(1))).collectAsMap

或者 不从文件读取,直接使用List类型数据演示

scala> var line_rdd = sc.parallelize(List[String]("k,v","key,value")).map(e=>(e.split(',')(0),e.split(',')(1))).collectAsMap
line_rdd: scala.collection.Map[String,String] = Map(k -> v, key -> value)

分析:collectAsMap 是行动操作的一种,可以将数据类型转换为Map类型,而collect是直接转为Array类型。

(3)场景三:从hive表中读取数据放到array数组中,其中每条数据转换为List类型。

scala> import org.apache.spark.{SparkConf, SparkContext}
scala> import org.apache.spark.sql.SparkSession scala> val conf = new SparkConf().setAppName("graph_spark@zky")
//设置本程序名称
scala> val hiveCtx: SparkSession = SparkSession.builder.config(conf).enableHiveSupport().getOrCreate()
//使用rdd函数转换格式
scala> var sql_file_result = hiveCtx.sql("select * from city limit 10").rdd
scala> sql_file_result
res10: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = MapPartitionsRDD[1187] at rdd at <console>:29 scala> sql_file_result.first
res11: org.apache.spark.sql.Row = [110000,北京市,110000,1,-911,2015-10-10 12:09:47,-911,2018-01-09 18:27:28,20181001000000]

分析:由于spark2.0版本丢弃了SQLContext(HiveContext),取而代之的是SparkSession。hdfs拉取的数据格式为 org.apache.spark.sql.Row,需要调用mkString("\t") 对其转换为String类型的rdd ,然后再转换为其他类型。

但当你的数据以制表符分隔,就像下面代码里一样,末尾字段值如果存在字符串""空时,建议在首尾加上 [ ] 标识符,因为制表符和末尾的空值都会被rdd 自动过滤掉。另外,不建议分隔符使用制表符分隔,在选用分隔符时确保数据中不会出现你指定的分隔符。

scala> var lines = sql_file_result.map(line => "["+line.mkString("\t")+"]")
lines: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[1189] at map at <console>:33 scala> lines.collect
res14: Array[String] = Array([110000 北京市 110000 1 -911 2015-10-10 12:09:47 -911 2018-01-09 18:27:28 20180123000000], [120000 天津市 120000 1 -911 2015-10-10 12:09:47 -911 2018-01-09 18:27:28 20180123000000],。。。

解析带[ ]的字符串转成list格式,split()函数中的-1是为确保空值不被过滤。

scala> var items = lines.map(line => line.substring(1,line.length-1).split("\t",-1).toList)
items: org.apache.spark.rdd.RDD[List[String]] = MapPartitionsRDD[1190] at map at <console>:35 scala> items.collect
res15: Array[List[String]] = Array(List(370101, 济南市, 370000, 1, -911, 1000-01-01 00:00:00, -911, 1000-01-01 00:00:00, 20180916000000), List(110000, 北京市, 110000, 1, -911, 2015-10-10 12:09:47, -911, 2018-01-09 18:27:28, 20180916000000),

  

  (4)场景四:从hive表中读取的数据存储为Map映射。

scala> var mid_data_rdd = hiveCtx.sql("select city_code,city_name from city limit 10").rdd
scala> mid_data_rdd.collect
res16: Array[org.apache.spark.sql.Row] = Array([110000,北京市], [120000,天津市], [130100,石家庄市], [130200,唐山市], [130300,秦皇岛市], [130400,邯郸市], [130500,邢台市], [130600,保定市], [130700,张家口市], [130800,承德市]) scala> var mid_data_map = mid_data_rdd.map(x => (x(0)->x(1).toString)).collectAsMap
mid_data_map: scala.collection.Map[Any,String] = Map(110000 -> 北京市, 130100 -> 石家庄市, 130300 -> 秦皇岛市, 120000 -> 天津市, 130500 -> 邢台市, 130700 -> 张家口市, 130200 -> 唐山市, 130400 -> 邯郸市, 130600 -> 保定市, 130800 -> 承德市) scala> var mid_data_map = mid_data_rdd.map(x => (x(0).toString->x(1).toString)).collectAsMap
mid_data_map: scala.collection.Map[String,String] = Map(130300 -> 秦皇岛市, 130600 -> 保定市, 130500 -> 邢台市, 130800 -> 承德市, 130200 -> 唐山市, 110000 -> 北京市, 130400 -> 邯郸市, 130700 -> 张家口市, 130100 -> 石家庄市, 120000 -> 天津市)
//如果想转换为array数组,试一下collect~
scala> var mid_data_map = mid_data_rdd.map(x => (x(0).toString->x(1).toString)).collect
mid_data_map: Array[(String, String)] = Array((110000,北京市), (120000,天津市), (130100,石家庄市), (130200,唐山市), (130300,秦皇岛市), (130400,邯郸市), (130500,邢台市), (130600,保定市), (130700,张家口市), (130800,承德市))

分析:可以关注下 toString函数~

(5)场景五:将数据通过写入临时表以存储到hive表

scala> val people = sc.parallelize(List(("1","mary"),("2","rose"),("3","jack")))
people: org.apache.spark.rdd.RDD[(String, String)] = ParallelCollectionRDD[1] at parallelize at <console>:24
scala> case class Person(id:String,name:String)
defined class Person
scala> var people_trans = people.map(item => Person(item._1,item._2))
people_trans: org.apache.spark.rdd.RDD[Person] = MapPartitionsRDD[2] at map at <console>:28
scala> val people_frame = people_trans.toDF()
people_frame: org.apache.spark.sql.DataFrame = [id: string, name: string] scala> people_frame.createOrReplaceTempView("person")
scala> import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.SparkSession scala> import org.apache.spark.SparkConf
import org.apache.spark.SparkConf scala> val conf = new SparkConf().setAppName("graph_spark@zhengkaiyu")
conf: org.apache.spark.SparkConf = org.apache.spark.SparkConf@534df4b scala> val hiveCtx: SparkSession = SparkSession.builder.config(conf).enableHiveSupport().getOrCreate()
18/11/19 21:47:11 WARN SparkSession$Builder: Using an existing SparkSession; some configuration may not take effect.
hiveCtx: org.apache.spark.sql.SparkSession = org.apache.spark.sql.SparkSession@18de437d scala> import hiveCtx.sql
import hiveCtx.sql scala> import hiveCtx.implicits._
import hiveCtx.implicits._ scala> sql("select * from person").collect
res6: Array[org.apache.spark.sql.Row] = Array([1,mary], [2,rose], [3,jack])
scala> sql("insert into 库名.hive表名 select * from person")

  分析:此例是基于case class来创建SchemaRDD,通过写入临时表,最后再插入到hive表中。除了这种方式还可以基于json格式来建临时表,见下例。其中spark2.1建立临时表时,将registerTempTable() 改为createOrReplaceTempView(),注意版本,要不会引起不必要的麻烦。

scala> import org.apache.spark.sql.SparkSession
scala> import org.apache.spark.SparkConf
scala> val conf = new SparkConf().setAppName("graph_spark@zhengkaiyu")
scala> val spark: SparkSession = SparkSession.builder.config(conf).enableHiveSupport().getOrCreate()
scala> val df = spark.read.json("examples/src/main/resources/people.json")
scala> df.show()
scala> df.createOrReplaceTempView("people")
scala> val sqlDF = spark.sql("SELECT * FROM people")
scala> sqlDF.show()

2、注意事项

(1)当启动交互环境 spark-shell 时,会出现较为诡异的事情,刚定义好的变量会被之前的同名变量所覆盖,猜想原因可能是内存不足导致。

(2)在scala代码中,建议if-else语句格式规范书写,否则会编译不正确。

if(条件){

}

(3)启动 spark-shell 时,注意指定的模式local、yarn。

3、常见错误的解决方法

不可序列化:org.apache.spark.SparkException: Task not serializable

解决方案1:继承java可序列化类

object Process extends java.io.Serializable{
}

参考博客:https://stackoverflow.com/questions/22592811/task-not-serializable-java-io-notserializableexception-when-calling-function-ou

通过从临时表中读取数据写入hive表时,会遇到错误:org.apache.spark.SparkException: Dynamic partition strict mode requires at least one static partition column. To turn this off set hive.exec.dynamic.partition.mode=nonstrict

解决方案:执行下面语句后再执行insert语句。

//val spark: SparkSession = SparkSession.builder.config(conf).enableHiveSupport().getOrCreate()
spark.sql("SET hive.exec.dynamic.partition = true") 
spark.sql("SET hive.exec.dynamic.partition.mode = nonstrict ")
spark.sql("SET hive.exec.max.dynamic.partitions.pernode = 400")

  

 

4、学习资料

《Spark快速大数据分析》王道远译,推荐理由:快速上手,实例代码有python、scala、java三种语言

《快学scala》

https://spark.apache.org/docs/2.1.0/sql-programming-guide.html#datasets-and-dataframes

https://tech.meituan.com/spark_tuning_pro.html

http://dblab.xmu.edu.cn/blog/spark-quick-start-guide/#more-577

spark.sql数据类型:http://spark.apache.org/docs/1.3.1/api/scala/index.html#org.apache.spark.sql.Row

小白学习Spark系列四:RDD踩坑总结(scala+spark2.1 sql常用方法)的更多相关文章

  1. 小白学习Spark系列三:RDD常用方法总结

    上一节简单介绍了Spark的基本原理以及如何调用spark进行打包一个独立应用,那么这节我们来学习下在spark中如何编程,同样先抛出以下几个问题. Spark支持的数据集,如何理解? Spark编程 ...

  2. 小白学习Spark系列二:spark应用打包傻瓜式教程(IntelliJ+maven 和 pycharm+jar)

    在做spark项目时,我们常常面临如何在本地将其打包,上传至装有spark服务器上运行的问题.下面是我在项目中尝试的两种方案,也踩了不少坑,两者相比,方案一比较简单,本博客提供的jar包适用于spar ...

  3. 小白学习Spark系列六:Spark调参优化

    前几节介绍了下常用的函数和常踩的坑以及如何打包程序,现在来说下如何调参优化.当我们开发完一个项目,测试完成后,就要提交到服务器上运行,但运行不稳定,老是抛出如下异常,这就很纳闷了呀,明明测试上没问题, ...

  4. 小白学习Spark系列一:Spark简介

    由于最近在工作中刚接触到scala和Spark,并且作为python中毒者,爬行过程很是艰难,所以这一系列分为几个部分记录下学习<Spark快速大数据分析>的知识点以及自己在工程中遇到的小 ...

  5. 小白学习Spark系列五:scala解析多级json格式字符串

    一.背景 处理json格式的字符串,key值一定为String类型,但value不确定是什么类型,也可能嵌套json字符串,以下是使用 JSON.parseFull 来解析多层json. 二.实例代码 ...

  6. Spring Boot 开发系列一 开发踩坑

    这是学习spring boot 的第二周,公司号称这玩意是啥都不会的新手就可以填空开发,于是决定上手一把,怎么说我也是搞了快七八年的.NET和.NETcore,没想到无情打脸,快被这个能填空开的IDE ...

  7. Spark系列(四)整体架构分析

    架构流程图 说明  Driver端流程说明(Standalone模式) 使用spark-submit提交Spark应用程序Application. 通过反射的方式创建和构造一个DriverActor进 ...

  8. spark实验(四)--RDD编程(1)

    一.实验目的 (1)熟悉 Spark 的 RDD 基本操作及键值对操作: (2)熟悉使用 RDD 编程解决实际具体问题的方法. 二.实验平台 操作系统:centos6.4 Spark 版本:1.5.0 ...

  9. spark 系列之一 RDD的使用

    spark中常用的两种数据类型,一个是RDD,一个是DataFrame,本篇主要介绍RDD的一些应用场景见代码本代码的应用场景是在spark本地调试(windows环境) /** * 创建 spark ...

随机推荐

  1. Spring MVC-表单(Form)标签-文本框(Text Box)示例(转载实践)

    以下内容翻译自:https://www.tutorialspoint.com/springmvc/springmvc_textbox.htm 说明:示例基于Spring MVC 4.1.6. 以下示例 ...

  2. RabbitMQ发布订阅实战-实现延时重试队列

    RabbitMQ是一款使用Erlang开发的开源消息队列.本文假设读者对RabbitMQ是什么已经有了基本的了解,如果你还不知道它是什么以及可以用来做什么,建议先从官网的 RabbitMQ Tutor ...

  3. HDU 5046

    同样是二分+DLX即可. #include <iostream> #include <cstdio> #include <cstring> #include < ...

  4. mysql 安装完毕后登陆不了mysql的 shell 即mysql&gt;遇到:ERROR 1045 (28000): Access denied for user 'root'@'localhost‘

    [root@hzswtb2-mpc ~]# mysql ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using pas ...

  5. AutoSharedLibrary -- 基于模板元编程技术的跨平台C++动态链接载入库

    基于模板元编程技术的跨平台C++动态链接载入库.通过模板技术,使用者仅需通过简单的宏,就可以使编译器在编译期自己主动生成载入动态链接库导出符号的代码,无不论什么额外的执行时开销. extern &qu ...

  6. HDOJ 题目5097 Page Rank(矩阵运算,模拟)

    Page Rank Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 100000/100000 K (Java/Others) Tota ...

  7. win7/WIN8.1(x64) 下使用MSDE WIN10不行

    通过强制安装(使用管理员权限),手工启动服务的方式,能够在其win7 win81上安装并使用MSDE Microsoft SQL Server 2000 Service Pack 4 Desktop ...

  8. 数据结构之---C++语言实现图的十字链表存储表示

    近期一直忙着考研复习,非常久都没有更新博客了.今天写一篇数据结构的存储. //有向图的十字链表存储表示 //杨鑫 #include <iostream> #include <cstd ...

  9. TestNG升级

    TestNG 6.5.1 or above is required,please update your TestNG or uncheck 'Use project TestNG jar' from ...

  10. ES 断路器——本质上保护OOM提前抛出异常而已

    监控fielddata使用了多少内存以及是否有数据被驱逐是非常重要的.大量的数据被驱逐会导致严重的资源问题以及不好的性能. Fielddata使用可以通过下面的方式来监控: 对于单个索引使用 {ref ...