一:性能优化之数据本地性
1,数据本地性对分布式系统的性能而言是一件最为重要的事情(之一),程序运行本身包含代码和数据两部分,单机版本一般情况下很少数据本地性的问题(因为数据在本地),但是对于单机版本的程序由于数据本地性有PROCESS_LOCAL和NODE_LOCAL之分,所以我们还是尽量的让数据处于PROCESS_LOCAL;Spark作为分布式系统更加注意数据本地性,在Spark中数据本地性分为PROCESS_LOCAL(同一个JVM)、NODE_LOCAL(同一个node,比如数据在HDFS上,但是和代码在同一个node)、NO_PREF、RACK_LOCAL(不在同一个server,但在同一个机架)、ANY(数据可能在任何地方,包括在其他网络环境中;例如说百度云,数据和计算集群不在同样的集群中,此时就是ANY一种表现);
2,对于ANY的情况,默认状态下性能会非常低下,此时强烈建议使用Tachyon;例如在百度云上为了确保计算速度,就在计算集群和存储集群之间加入了Tachyon,通过Tachyon来从远程抓取数据,而Spark基于Tachyon来进行计算,这就更好的满足了数据本地性;
3,如果数据是PROCESS_LOCAL,但是此时并没有空闲的Core来运行我们的Task,此时Task就要等待,例如等待3000ms,3000ms内如果能够运行待运行的Task则直接运行,如果超过了3000ms,此时数据本地性就要退而求其次采用NODE_LOCAL,同样的道理NODE_LOCAL也会等待的超时时间,以此类推…
4,如何配置Locality呢?可以统一采用spark.locality.wait来设置(例如设置5000ms),当然你可以分别设置spark.locality.wait.process,spark.locality.wait.node, spark.locality.wait.rack等;一般的具体设置是Locality优先级越高则可以设置越高的等待超时时间;
二:RDD的自定义(以Spark on HBase为例)
1,第一步是定义RDD.getPartitions的实现:
a)createRelation具体确定HBase的链接方式和具体访问的表;
b)然后通过HBase的API来获取Region的List;
c)可以过滤出有效地数据;
d)最后返回Region的Array[Partition],也就是说一个Partition处理一个Region的数据,为更佳的数据本地性打下基础;
2,第二步是RDD.getPreferredLocations
a)根据Split包括的Region信息来确定Region具体在什么节点上,这样Task在调度的时候就可以优先被调度到Region所在的机器上,最大化的提高数据本地性;
3,第三步是RDD.compute
a) 根据Split中的Region等信息调用HBase的API来进行操作(主要是查询)
1.作业:从网络上查询RDD封装MySQL的具体实现
在Spark中提供了一个JdbcRDD类,该RDD就是读取JDBC中的数据并转换成RDD,之后我们就可以对该RDD进行各种的操作。我们先看看该类的构造函数:
JdbcRDD[T: ClassTag](
sc: SparkContext,
getConnection: () => Connection,
sql: String,
lowerBound: Long,
upperBound: Long, numPartitions: Int, mapRow: (ResultSet) => T = JdbcRDD.resultSetToObjectArray _)
这个类带了很多参数,关于这个函数的各个参数的含义,我觉得直接看英文就可以很好的理解,如下:
1、getConnection 返回一个已经打开的结构化数据库连接,JdbcRDD会自动维护关闭。
2、sql 是查询语句,此查询语句必须包含两处占位符?来作为分割数据库ResulSet的参数,例如:”select title, author from books where ? < = id and id <= ?”
3、lowerBound, upperBound, numPartitions 分别为第一、第二占位符,partition的个数。例如,给出lowebound 1,upperbound 20, numpartitions 2,则查询分别为(1, 10)与(11, 20)
4、mapRow 是转换函数,将返回的ResultSet转成RDD需用的单行数据,此处可以选择Array或其他,也可以是自定义的case class。默认的是将ResultSet 转换成一个Object数组。