scala Map 排序
对于scala Map数据的排序,使用 scala.collection.immutable.ListMap 和 sortWiht(sortBy),具体用法如下
val sortedScore = ListMap(outScore.toSeq.sortWith(_._2._5>_._2._5):_*)
即, 使用outScore value的第5个值按降序排列
spark Logger 的输出
spark rdd数据为惰性数据,在map的过程中无法打印中间数据,需要通过行为操作将其转化为scala数据格式,然后遍历打印。
spark rdd 转换为map
contenttypeHit是一个spark pair RDD,需要转化为一个Map,maxHit = contenttypeHit.collect()
执行后获取的是一个Array,不能通过key值获取value,因此先定义一个可变映射val score = new scala.collection.mutable.HashMap[Int, Long]
,然后对maxHit执行map操作更新score的值,maxHit.map(x => if(x._2==0) score(x._1)=1L else score(x._1)=x._2)
,缺点是maxHit得到的是一个unit值。 这是一个蠢方法,正确的方法是 val maxHit = contenttypeHit.map(x=>(x._1, x._2)).collectAsMap()
转换操作中不要掺杂行为操作
正则匹配貌似在转化操作中也无法正常执行,会抛出空指针的异常,暂时还未确定 更正:正则表达式可以在转化操作中进行,空指针是因为读数据返回的空值不是“”而是Null
spark flatmap
val subtype_pid_score = sub_type.filter(v => v._4.isInstanceOf[String])
.flatMap(vv => vv._4.split("|")
.filter(vvf => vvf.length==8)
.map(vvv => (vvv.toLong,(vv._1, pid_score.getOrElse(vv._1, 0.0))))
)
flatMap操作后产生了[[“12112”, “343434”], [“8787”, “77987”]]并展开成[“12112”, “343434”,”8787”, “77987”, vvv] 然后进行map操作是对所有展开后的元素,并且可对应到vv。
HiveContext 读取 JDBC
Jdbc的读取分为两种方法,一种是使用sqlContext,一种是使用HiveContext,前种方法返回的是一个rdd,后种方法返回的是dataFrame
JDBC的sqlContext读取
def getNewsDataRankInfo(sqlContext: SQLContext) = {
sqlContext.read.jdbc(JDBCUrl, "t_news_info", Prop).registerTempTable("t_news_info")
sqlContext.sql("select f_news_id as f_pid, UNIX_TIMESTAMP(f_release_time) as f_time, f_sub_type from t_news_info where f_status = 65002")
}
sqlContext.read.jdbc相当于将JDBC的配置读取到sqlContext中,然后使用sqlContext类提供了一个sql方法,使用sql语句select需要的数据。返回值是一个DataFrame类实例。
JDBC的hiveContext读取
val hiveCtx = new HiveContext(sc)
val url = "jdbc:mysql://192.168.36.94/homed_spider"
val tableName = "t_spider_program_final"
import java.util.Properties
val connProperties = new java.util.Properties
connProperties.setProperty("user", "root")
connProperties.setProperty("password", "123456")
val jdbcDF = hiveContext.read.jdbc(url, tableName, connProperties)
返回的值jdbcDF即整个表,类型为DataFrame
Map的更新
scala中映射分为两种
- 可变映射 import scala.collection.mutable.HashMap
- 不变映射 val score = Map((“a”,1))
对于可变映射,值的更新可以直接通过key赋值,对于不变映射你无法更新它的值,但是你 可以得到一个新的
val newScore = score + ("v"->12, "a"->13)
新赋的值将覆盖原来的值,如果想通过Map来更新Map,使用++运算符
val nScore = score ++ newScore
groupBy
对于rdd使用groupBy,可以生成以指定项为key的Map
val exp =Array((都市,0.3), (爱情,0.3), (纪录,0.28),(都市, 0.4))
val expMap =exp.groupBy(x=>x._1).map(v=>(v._1, v._2.map(x=>x._2)))
生成的数据格式为 Map(都市->(0.3,0,4),爱情->(0.3),记录->(0.28))
sample
sample对RDD采样,以及是否替换,调用方法为
rdd.sample(false,0.5)
- true 表示有放回的取样,即该次抽样所得样本有重复
- false 表示无放回的取样,抽样无重复
scala 时间戳与时间互转
依赖包:
import java.util.Date
import java.text.SimpleDateFormat
1.时间戳转时间字符串
def tmiestampTostr(ts :Long) = {
val SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
SimpleDateFormat.format(new Date(ts*1000L))
}
2.时间转时间戳
def getTimestamp(t: String):Long = {
val date1 = """(\d\d\d\d)(\d\d)(\d\d)""".r
val date2 = """(\d\d\d\d)-(\d\d)-(\d\d).*""".r
val fm1 = new SimpleDateFormat("yyyyMMdd")
val fm2 = new SimpleDateFormat("y-M-d")
t match {
case date1(y, m, d) => fm1.parse(t).getTime / 1000
case date2(y, m, d) => fm2.parse(t).getTime / 1000
case _ => {
//Logger.info(s"can't parse time from this string:$t")
//curTime.set(1970, 0, 0)
0L
}
}
}
scala 函数柯里化
scala> def sum(x:Int)(y:Int)=x+y
sum: (x: Int)(y: Int)Int
scala> val second = sum(1)_
second: Int => Int =
scala> second(2)
res1: Int = 3
柯里化函数执行时,分解为两个函数执行,步骤与下面的方法调用过程类似
scala> def first(x:Int) = (y:Int)=>x+y
first: (x: Int)Int => Int
scala> val second = first(1)
second: Int => Int =
scala> second(2)
res4: Int = 3
curry化最大的意义在于把多个参数的function等价转化成多个单参数function的级联,这样所有的函数就都统一了,方便做lambda演算。 在scala里,curry化对类型推演也有帮助,scala的类型推演是局部的,在同一个参数列表中后面的参数不能借助前面的参数类型进行推演,curry化以后,放在两个参数列表里,后面一个参数列表里的参数可以借助前面一个参数列表里的参数类型进行推演。这就是为什么 foldLeft这种函数的定义都是curry的形式
Top操作取前几项
top方法返回一个由原RDD中前N大的元素构成的RDD,并且可以指定由哪个数据排序
val t = sc.parallelize(Array(("a",1),("b",2),("c",3),("d",4),("e",7),("f",1)))
scala> t.top(2)(Ordering.by(_._2))
res14: Array[(String, Int)] = Array((e,7), (d,4))
Ordering.by()指定由哪项数据排序,当取N得数目大于数据长度时,取全部数据,不会报错
RDD中嵌套RDD
对于一个pair RDD, 其value为array,像将其变换为rdd后toDF,然后与usrInfo做连接操作,但是在mapValues过程中将array通过sc.parallelize转换为rdd执行时会出现 java.lang.NullPointerException的问题。但是,暂时还不知道不能这样操作还是某个步骤出错了
scala> val r1 = sc.parallelize(Array(1,2,3,4))
r1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at :27
scala> val r2 = sc.parallelize(Array(5,6,7,8))
r2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[1] at parallelize at :27
scala> val r = sc.parallelize(Array((1,2,r1),(2,3,r2)))
r: org.apache.spark.rdd.RDD[(Int, Int, org.apache.spark.rdd.RDD[Int])] = ParallelCollectionRDD[2] at parallelize at :31
scala> r.first
res0: (Int, Int, org.apache.spark.rdd.RDD[Int]) = (1,2,ParallelCollectionRDD[0] at )
scala> r
res1: org.apache.spark.rdd.RDD[(Int, Int, org.apache.spark.rdd.RDD[Int])] = ParallelCollectionRDD[2] at parallelize at :31
但是在spark-shell中实验所得到的是一个(Int, org.apache.spark.rdd.RDD[Int])
,证明rdd的嵌套是可行的
但是在实际编译过程中,会出现错误
[error] type mismatch;
[error] found : Array[?B]
[error] required: scala.collection.GenTraversableOnce[?]
[error] Note that implicit conversions are not applicable because they are ambiguous:
[error] both method booleanArrayOps in object Predef of type (xs: Array[Boolean])scala.collection.mutable.ArrayOps[Boolean]
[error] and method byteArrayOps in object Predef of type (xs: Array[Byte])scala.collection.mutable.ArrayOps[Byte]
[error] are possible conversion functions from Array[?B] to scala.collection.GenTraversableOnce[?]
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 6 s, completed 2017-10-27 11:40:59
应该是无法识别Array中的元素。shell中的格式为RDD[(Int, Int, org.apache.spark.rdd.RDD[Int])]
,但是在程序中所得到的格式为RDD[Array[(Long, Int, RDD[String])]]
,很明显,程序中是在Array中嵌套了一个RDD,正确格式应该为RDD[(Long, Int, RDD[String])]
org.apache.spark.SparkException: RDD transformations and actions can only be invoked by the driver, not inside of other transformations; for example, rdd1.map(x => rdd2.values.count() * x) is invalid because the values transformation and count action cannot be performed inside of the rdd1.map transformation. For more information, see SPARK-5063.
rdd的嵌套可以执行,但是无法实现,内部的rdd无法执行行为操作。根据SPARK-5063,RDD的嵌套操作是不被允许的。
SPARK-5063 relates to better error messages when trying to nest RDD operations, which is not supported.
It’s a usability issue, not a functional one. The root cause is the nesting of RDD operations and the solution is to break that up.
Here we are trying a join of dRDD and mRDD. If the size of mRDD is large, a rdd.join would be the recommended way otherwise, if mRDD is small, i.e. fits in memory of each executor, we could collect it, broadcast it and do a ‘map-side’ join.
在spark中根据Key值找Value
在一般程序中,常见的操作是,得到一系列的key值,然后需要从键值对中找到对应的Value进而通过一些了变换得到所需要的值。但是在spark中,即使你得到的是pair RDD,你也不能通过经典的 key:value方法获取值,对于pair RDD,lookup操作可以通过key值得到相应的value,但是lookup是行为操作,所得到的是一个Array或者其他的scala的格式。即对于一个pair RDD,只有将其collect后才能实现key与value的映射。
val kindScoreMap = kindWeight.toMap //用户kind权重映射 kind : weight,将权重视为得分
val kinds = kindScoreMap.keys.toArray
val kindRankContain = kindRank.filter(x => kinds.contains(x._1)) //包含用户Kind的节目
val contentRank = kindRankContain.flatMap(x => x._2.map(v => (v._1, (kindScoreMap(x._1), v._3))) //item, (score, series)
.groupBy(vv => vv._1).map(vvv => {val add = vvv._2.head._2._2;(vvv._1,add+vvv._2.map(z =>z._2._1).sum)}).toArray
//按item聚合,并且将评分相加然后加上单集与剧集的分数
).sortBy(x => x._2) //按评分排序
因此,类似于将KindScoreMap带入contentRank的转换操作,以及vvv._2.head等行为操作嵌套与转换操作中都是不可行的。
因此,对于spark中类似通过key值找value的操作,必须通过join操作来实现
对于用户聚类群组RDD[(Int, Array[Long])]
我们想通过RDD[(Long(usrId), Array[(Int, Array[(Int, Int)])])]
得到每个用户的Kind权值信息同时保存用户的分组,因此我们可以val usrCluster = clusterRes.flatMap(x => x._2.map(v => (v, x._1)))
将聚类群组[(clusterId, Array[usrId])]
展开为(usrId, clusterId)
,然后与用户的kind信息join,得到所需要的数据。
Spark写数据库
碰到的问题是,在更新Kind的时候,uuid作为主键应当设为自增
WARN HiveConf: HiveConf of name hive.stats.map.parallelism does not exist
hive.stats.map.parallelism
Default Value: 1
Added In: Hive 0.13 with HIVE-5369
Removed In: Hive 0.14 with HIVE-7156
The Hive/Tez optimizer estimates the data size flowing through each of the operators. For the GROUPBY operator, to accurately compute the data size map-side parallelism needs to be known. By default, this value is set to 1 since the optimizer is not aware of the number of mappers during compile-time. This Hive configuration property can be used to specify the number of mappers for data size computation of the GROUPBY operator. (This configuration property was removed in release 0.14.0.)
spark监控
http://192.168.3.100:50070/
可以浏览HDFS配置
对于YARN集群模式来说,应用的驱动器程序会运行在集群内部,应当通过YARN的资源管理器来访问用户界面,YARN的资源管理器会把请求直接转发给驱动程序。
192.168.3.101:8088
8088是YARN的资源管理器的页面监控地址,具体设置在yarn-site.xml
因为YARN资源管理器的访问比较复杂,因此将集群管理员从YARN切换到SPARK独立管理员,即--master spark//:master:7077
而master
的地址取自spark-env.xml
文件export SPARK_MASTER_IP=192.168.2.200
,然后就可以通过master:4040监控任务
关于YARN集群管理的问题,在后面对Hadoop生态有了更清晰打的认识之后再设置
scala 函数作用域的问题
在引用scala.math函数时,在最外围import时,函数中并不能定位到,例如ceil(num)
并不会报错,但是在编译过程中它返回的却是一个Any
在设置spark启动资源时,不同模式设置方法不一样
spark的配置项遵循
执行节点的内存在任何部署模式是下都可以通过spark.executor.memory或者–executor-memory设置。
执行器节点的数目以及每个之形象墙进程的核心数配置则取决于各种部署模式。
- 在YARN模式下,可以通过spark.executor.cores或–executor-memory标记来设置执行器节点的核心数,通过–num-exexutors设置执行节点的总数
- 在Mesos和独立模式下,spark自会从调度器提供的资源中获取尽可能多的核心用于执行器节点,但是可以通过设置spark.cores.max来限制执行器节点所使用的核心总数。
Spark分区器HashPartitioner和RangePartitioner
分区与任务并行度的关系。 HashPartitioner和RangePartitioner都是针对key而言的,而所得分区数也为任务的并行度。
spark 读取hive, input path does not exist
在spark社区上发现了讨论此Bug的话题
SPARK-15044
1.Only for parquet tables.
set spark.sql.hive.convertMetastoreParquet=true;2.That is to say the partition exists in hive table, but the related path was removed manually, and spark 1.6.1 would throw exception
3.This exception is caused by “the HiveContext cache the metadata of the table” ;When you delete the hdfs,
you must refresh table by using sqlContext.refreshTable(tableName);
4.refreshTable(tableName) 将指定表的所有元数据缓存信息进行无效操作和刷新操作。Spark SQL或其他的数据源可能会缓存一张固定表的元数据,比如块位置。当这些元数据不是通过SPark SQL 进行更改时,可以使用本方法将当前内存中的元数据缓存无效化
import org.apache.spark.sql.hive.HiveContext
hiveContext.refreshTable("t_user_profile_rec_final")
val userInfo = hiveContext.sql("select * from t_user_profile_rec_final")
但是,还是会出错….
不过是不存在的路径从2017-09-01变成了2017-08-14,应该是refresh的缘故
最终解决方案
指定分区读取,
scala> val userInfo = hiveContext.sql(s"select * from t_user_profile_rec_final where day >'2017-11-09'")
day是建立hive的分区参数,即partitionBy day
rdd转为DataFrame
val rowsRdd = usrTimeKindWeight.mapValues(vv =>(range(0, 25).map(i => (i,Array[(String, Double)]())).toMap ++ vv.flatMap(x => Map((x._1, x._2))).toMap).toSeq.sortWith(_._1<_._1)
.map(vvv=>vvv._2).toList).map(x => (x._1.toString +: x._2.map(vv => vv.mkString("|"))).toArray)
.map(d => Row(d(0), d(1), d(2), d(3), d(4), d(5), d(6), d(7), d(8), d(9), d(10), d(11), d(12),
d(13), d(14), d(15), d(16), d(17), d(18), d(19), d(20), d(21), d(22), d(23), d(24)))
import org.apache.spark.sql.types.{StructType,StructField,StringType}
val schemaString = "usrId cl0 cl1 cl2 cl3 cl4 cl5 cl6 cl7 cl8 cl9 cl10 cl11 cl12 cl13 cl14 cl15 cl16 cl17 cl18 cl19 cl20 cl21 cl22 cl23 "
val schema =
StructType(
schemaString.split(" ").map(fieldName => StructField(fieldName, StringType, true)))
val sqlContext = new SQLContext(sc)
val usrDataFrame = sqlContext.createDataFrame(rowsRdd, schema)
DF只接受22列
Breaks
A class that can be instantiated for the break control abstraction
val mybreaks = new Breaks
import mybreaks.{break, breakable}
breakable {
for (...) {
if (...) break()
}
}
使用maven编译时出现的错误
Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.3:compile (default-compile)
需要在pom.xml中修改插件的设置
src/main/scala
org.apache.maven.plugins
maven-compiler-plugin
3.3
true
true
C:\Program Files\Java\jdk1.8.0_144\bin\javac
1.8
UTF-8
spark环境配置
配置spark环境的主要几个文件如下
spark-env.sh
,这个文件主要设置与hadoop的关系
export JAVA_HOME=/usr/java/jdk1.7.0_55
export SPARK_DIST_CLASSPATH=/r2/hadoop/hadoop-2.6.4/etc/hadoop:/r2/hadoop/hadoop-2.6.4/share/hadoop/common/lib/*:/r2/hadoop/hadoop-2.6.4/share/hadoop/common/*:/r2/hadoop/hadoop-2.6.4/share/hadoop/hdfs:/r2/hadoop/hadoop-2.6.4/share/hadoop/hdfs/lib/*:/r2/hadoop/hadoop-2.6.4/share/hadoop/hdfs/*:/r2/hadoop/hadoop-2.6.4/share/hadoop/yarn/lib/*:/r2/hadoop/hadoop-2.6.4/share/hadoop/yarn/*:/r2/hadoop/hadoop-2.6.4/share/hadoop/mapreduce/lib/*:/r2/hadoop/hadoop-2.6.4/share/hadoop/mapreduce/*:/hadoop/hadoop-2.6.4/contrib/capacity-scheduler/*.jar
export HADOOP_CONF_DIR=/hadoop/hadoop-2.6.4/etc/hadoop/
export SPARK_HOME=/homed/spark
export SPARK_MASTER_IP=192.168.2.100
export SPARK_MASTER_WEBUI_PORT=9090
export SPARK_EXECUTOR_MEMORY=5g
export SPARK_WORKER_OPTS='-Dspark.worker.cleanup.enabled=true -Dspark.worker.cleanup.interval=1800'
export SPARK_HISTORY_OPTS='-Dspark.history.fs.logDirectory=file:/homed/spark/work'
export HIVE_HOME=/hadoop/hive/apache-hive-0.13.1-bin
export SPARK_MASTER_OPTS="$SPARK_MASTER_OPTS -Djava.library.path=/hadoop/hadoop-2.6.4/lib/native"
export SPARK_WORKER_OPTS="$SPART_WORKER_OPTS -Djava.library.path=/hadoop/hadoop-2.6.4/lib/native"
export SPARK_SUBMIT_OPTS="$SPARK_SUBMIT_OPTS -Djava.library.path=/hadoop/hadoop-2.6.4/lib/native"
export SPARK_CLASSPATH="$SPARK_CLASSPATH:/r2/hadoop/hive/apache-hive-0.13.1-bin/lib/mysql-connector-java-commercial-5.1.25-bin.jar"
core-site.xml, hdfs-site.xml
,这两个文件是hadoop的设置文件,路径在hadoop/hadoop-2.6.4/etc/hadoop
下hive-site.xml
,Hive的设置文件 修改Hive的hive-site.xml文件得到正确的元数据库
Maven编译spark程序
默认的mvn install生成的jar是不带主类入口的,需要在maven-compile-plugin中设置主类。
org.apache.maven.plugins
maven-jar-plugin
com.profile.main.UserProfile
true
lib/
sqlContext与hiveContext
sqlContext创建的临时表使用hiveContext读取 table not found
发现由于自己的dataframe是用SQLContext创建的,而用HiveContext是无法访问的。这就涉及到registerTempTable生命周期的问题,以前都没细看,其生命周期只在所定义的sqlContext或hiveContext实例之中。换而言之,在一个sqlontext(或hiveContext)中registerTempTable的表不能在另一个sqlContext(或hiveContext)中使用。
因此,在spark中新建sqlContext时可以这样
val sqlContext = new org.apache.spark.sql.hive.HiveContext(sc)
import sqlContext.implicits._
DF写hive部分数据总为NULL
uninoDF.write.mode("append").saveAsTable("t_user_info_b")
写hive表,userID等若干项都为NULLsqlContext.sql("create table t_user_info_a as select * from t_user_tmp")
写入hive表数据没有问题,但是会抛出一个异常org.apache.thrift.TApplicationException: Invalid method name: 'alter_table_with_cascade'
sqlContext.sql("INSERT INTO TABLE t_user_info select * from t_user_tmp")
插入到t_user_info
中数据没有问题,但是依然会抛出之前那个异常sqlContext.sql("INSERT INTO TABLE t_user_info_b select * from t_user_tmp")
插入到t_user_info_b
中没有抛出异常,但是数据又为NULLdesc formatted table_name;
查看,主要是storage infomation不同。并且t_user_info_b
的table_info
中更多关于表中元素的信息。t_user_info_b:
/# Storage Information
SerDe Library: org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe
InputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat
OutputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat
t_user_info:
# Storage Information
SerDe Library: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
InputFormat: org.apache.hadoop.mapred.TextInputFormat
OutputFormat: org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat
考虑自建表然后插入,t_user_info
的建表语句为
CREATE TABLE `t_user_info`(
`userid` string,
`time` string,
`terminal` string,
`programid` string,
`programname` string,
`playtime` int,
`duration` string,
`contenttype` string,
`subtype` string,
`behavior` string,
`day` string)
ROW FORMAT SERDE
'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat'
LOCATION
'hdfs://hdfs260-Yunying/user/hive/warehouse/t_user_info'
TBLPROPERTIES (
'transient_lastDdlTime'='1511777732')
参考t_user_profile_rec_final
的建表语句
CREATE EXTERNAL TABLE `t_user_profile_rec_final`(
`start_time` string,
`user_id` string,
`end_time` string,
`terminal` string,
`program_id` bigint,
`program_name` string,
`play_time` int,
`duration` string,
`contenttype` string,
`subtype` string,
`behavior` string)
PARTITIONED BY (
`day` string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
'hdfs://hdfs260-Yunying/user/hive/warehouse/t_user_profile_rec_final'
TBLPROPERTIES (
'transient_lastDdlTime'='1506770494')
现在解决Invalid method name: 'alter_table_with_cascade'
这个问题,在网上查询了一番,应该就是spark编译的 spark.sql.hive.metastore
与hive本身的metasore不一致,服务器上使用的Hive是0.13.1
alter\_table\_with_cascade
sparkConf.set("spark.sql.hive.metastore.version", "0.13.1")
sparkConf.set("spark.sql.hive.metastore.jars", "maven")
按照上述方法设置metastore的版本,但是maven始终无法下载,使用指定路径
sparkConf.set("spark.sql.hive.metastore.version", "0.13.1")
sparkConf.set("spark.sql.hive.metastore.jars", "/hadoop/hive/apache-hive-0.13.1-bin/lib/hive-metastore-0.13.1.jar")
很明显,结果还是不行,后来仔细看了下debug里面的错误原因,应该是需要把Hadoop的jar都添加到路径中
sparkConf.set("spark.sql.hive.metastore.version", "0.13.1")
sparkConf.set("spark.sql.hive.metastore.jars", "/hadoop/hadoop-2.6.4/share/hadoop/mapreduce/*:" +
"/hadoop/hadoop-2.6.4/share/hadoop/mapreduce/lib/*:/hadoop/hadoop-2.6.4/share/hadoop/common/*:" +
"/hadoop/hadoop-2.6.4/share/hadoop/common/lib/*:/hadoop/hadoop-2.6.4/share/hadoop/hdfs/*:" +
"/hadoop/hadoop-2.6.4/share/hadoop/hdfs/lib/*:/hadoop/hadoop-2.6.4/share/hadoop/yarn/*:" +
"/hadoop/hadoop-2.6.4/share/hadoop/yarn/lib/*:/hadoop/hive/apache-hive-0.13.1-bin/lib/*:")
再运行,Bingo!
Exception in thread “main” java.lang.OutOfMemoryError: PermGen space
在读取用户日志时,每次读到12天时就会出现内存溢出的异常,经查询,应该是下面所描述的问题
在Spark中使用hql方法执行hive语句时,由于其在查询过程中调用的是Hive的获取元数据信息、SQL解析,并且使用Cglib等进行序列化反序列化,中间可能产生较多的class文件,导致JVM中的持久代使用较多,如果配置不当,可能引起类似于如下的OOM问题:
因为在程序中,每一天的日志都会注册成为一个临时表,每一天的epg数据也会注册成为一个临时表,而在写hive时,也会注册一个临时表,因此,处理一天的数据就要注册3个临时表。
所以为了避免该问题,将sql语句执行的逻辑改为dataframe来执行。
但是从结果来看,在spark UI中,依然产生了很多SQL句柄,怀疑是val sqlContext = new HiveContext(sc)
每次申明一个sqlCcontext都看做是一个sql句柄。因此,只在程序入口申请一个sqlContext然后传递给下面的函数,观察spark UI,sql句柄果然少了下来。
对于创建的sqlContext,其生命周期的问题需要以后关注一下
The root scratch dir: /tmp/hive on HDFS should be writable. Current permissions are: rw-rw-rw-
针对这个异常,网上很多方法都是修改文件的权限,但是个人总觉得不是很对,然后发现了一个新的说法
Don’t do chmod (777)… The correct is (733):
Hive 0.14.0 and later: HDFS root scratch directory for Hive jobs, which gets created with write all (733) permission. For each connecting user, an HDFS scratch directory hive.exec.scratchdir/iscreatedwith {hive.scratch.dir.permission}.
Try to do this with hdfs user:
hdfs dfs -mkdir /tmp/hive
hdfs dfs -chown hive /tmp/hive/$HADOOP_USER_NAME
hdfs dfs -chmod 733 /tmp/hive/$HADOOP_USER_NAME
hdfs dfs -mkdir /tmp/hive/$HADOOP_USER_NAME
hdfs dfs -chown $HADOOP_USER_NAME /tmp/hive/$HADOOP_USER_NAME
hdfs dfs -chmod 700 /tmp/hive/$HADOOP_USER_NAME
This works, instead you can change scratchdir path with (from hive):
set hive.exec.scratchdir=/somedir_with_permission/subdir…
然后看了一下集群上master的设置
hive.exec.scratchdir
/tmp/hive-${user.name}
Scratch space for Hive jobs
hive.exec.local.scratchdir
/tmp/${user.name}
Local scratch space for Hive jobs
很明显,是在tmp下根据user.name建立的临时文件夹
官方说法在这里
AdminManual Configuration
但是依然会抛出之前那个异常,只好在尝试修改文件权限了
hadoop fs -ls chmod 777 /tmp/hive-root
改完之后果然没有报错了…