1.对于确定长度的数组,用模式匹配来获取每一位的数据
正例:val Array(name,age) = x.split(",")
反例: val array = x.split(","); val name = array(0); val age = array(1)
2.DataFrame获取数据,使用用getAs[T](fieldName : scala.Predef.String)方法
正例:val name = row.getAs[String]("name")
反例:val name = row.getString(0) //当数据源的列发生错位时,此用法会导致获取到错误的数据
3.尽可能的使用DataSet,而不是DataFrame。DataFrame的校验是运行校验,而DataSet可以做到编译校验
4.SparkSql的编写,尽可能的遵循SQL规范
5.字符串拼接,请使用可插入字符串
正例:"my name is ${name}"
反例:"my name is " + name
6.case class 与DataSet结合使用时,不需要自定义序列化类,DataSet的数据类型是一种很轻量且高效的编码器Encoder。
7.尽可能的使用SparkSql,DataFrame,DataSet,而不是RDD,由于Catalyst和Tungsten的存在,绝大多数情况下结构化编程都要比RDD运行效率高
8.对于分区后内部排序的场景,可以尝试rdd.partitionBy()转dataframe,再sortWithinPartitions()。在排序很耗时的情况下,由于Tungsten的存在,该方法比rdd.repartitionAndSortWithinPartitions要高效(UnsafeExternalSorter
比ExternalSorter更高效)。
9.SQL是一种很高效的业务开发模式,不要抗拒,但是一定要写出高效的SQL,以及明白SQL背后的执行原理
10.是写SQL,还是DataSet,取决于开发团队的技术栈。复杂SQL容易写错,DEBUG较繁琐,需不断提交任务方能试错,而DataSet可以在编译时发现问题
11.生产环境的离线报表不要使用动态资源分配,报表查询,SQL交互计算可考虑使用,须限制好最大申请资源
12.列式存储,如(parquet),尽可能的对数据排序,可减少存储空间占用
13.多条件分支的判断,尽可能用模式匹配代替if else
14.尽可能的不要在Spark直接入关系型数据库,若一定要这么做,请控制好task数量,以免对数据库产生较大的压力
15.在Spark算子内调用公共方法(如mappartition里写库,调用公用的数据库相关方法),需要将函数写为线程安全的
16.having不要和开窗函数配合使用时,采用子查询的方式。(在Spark2.4以后非子查询的使用会报错)
正例:select * from (select * ,row_number() over (partitin by class order by age) as rank from table) as t where rank = 1
反例:select * ,row_number() over (partitin by class order by age) as rank from table having rank = 1
17.关于缓存的使用,确定数据足够小的话可以使用MEMORY_ONLY。另外,DISK_ONLY也很不错,虽然数据存于磁盘,但是再次读取免去了RDD序列化的开销,通常比再次从hdfs读取数据要高效很多。
18.SparkSQL的cbo功能建议在SQL交互查询时开启,对于每日的离线任务则不建议开启,CBO的本质是基于统计学来进行优化,生成环境的离线任务在部署前理应抽样统计好自己的数据规范和分布。
19.Spark调优前先明白自己的任务是什么类型(IO密集型,内存型,CPU密集型)的,不要盲目调优,
20.Spark离线任务是否允许成功,尽可能的使用类似_SUCCESS的标识来判断,而不是信号量,spark的有时候无法正常退出,比如shutdown hook超时,此时信号量为异常,但实际任务已经允许成功
21.非必要不开启堆外内存,堆外内存会导致作业不稳定,隐患较大,Spark sql经过钨丝计划优化后的一般来讲使用堆内也不比堆外内存效率差
22.spark单个executor执行过慢不一定是数据倾斜的原因,可能是map内的逻辑针对特性key不友好,亦或者executor所在的节点性能欠佳
23.对于工具类,设计函数时要合理使用枚举
正例:def process(deviceType:Enumeration,deviceValue:String)
反例:def process(deviceType:String,deviceValue:String)
24.对于dataframe,lamda表达式不要和dsl混用,因为lamda表达式会多一次jvm序列化,且有可能不被catalyst优化感知
正例:df.filter("age > 30")
反例:df.filter(_.getAs[String]("age")>30)