最近遇到一个颇感困扰的问题,因为之前的小脚本已经成型了,但效率挺低,考虑优化的事情。
优化大致从三个方向考虑:脚本里面的逻辑优化,脚本里边的sql语句优化,集群优化。
因为脚本整体很简单,目前分析指标也很简单,对于脚本以及分析语句再次核查,没发现值得优化的地方。而整个集群的优化,又只有3台机器,数据量一天1G左右,仿佛也没优化的必要性。可就这么小的数据量,这么简单的查询,执行起来却需要1小时多的时间,在向某高手请教时,给出的方向是指向了机器配置太低?可现实就在于机器是无法改变的。
在同时,我开始纠结的一个问题(1)小文件问题,因为我们的hdfs上存放的源数据表对应的全部是小文件,每个在几k到几十M之间,这些小文件会造成占用主节点过多的内存,不确定对速度的影响。试着将1个目录的小文件合并到一个大文件,重新执行语句,依然没有什么明显的速度提升,在某条语句的几毫秒的提升,可能被另外一语句的几十毫秒的倒退所抵消了,而这细微的差别很可能与机器当时的状态有关。同时,我注意到一个问题(2)每次都的sql执行的job,都只有一个map在执行。按照网上的资料,hadoop执行的map个数是根据block数来确定的,而当块大小按照默认的64m,我们的日志文件绝大多数小于block size,那么应该每个文件占用一个block,那么有大量小文件,就会对应大量的map数,事实却只有一个。为此请教了群里一个做hive方面的人,map数确切来说不是根据block来划分的,而是根据split来决定的,而split到底怎么得到,就得去源码里面看。 找了网上的一些资料,有一些对getsplits这个方法中代码的解释,总体来说,就是split的划分,通过包括block size在内的三个值来综合比较,但如果没有特殊设置的话,综合比较得到的结果就是block size,这样的话,split的个数依然是block的个数。问题又回到原点。(在hive cli命令行,通过set dfs.block.size;可以看到集群当前配置的块大小)
没有头绪,我就瞎试,唉,看来有功夫读下源码才是王道。
(1)把文件的大小提高,将一个目录下的文件加两个大的加到600m多,发现map数变为3,再加一个,加到将近900m,map变为4个。可惜600和900都不是64m的倍数
通过看那个getsplits方法,我了解到split分块还跟不同的format有关系, 我开始考虑是否hive的任务执行跟hadoop的不太一样,hive采用的format里边的getsplits方法跟hadoop里面的原理差距很大?
(2)通过50030端口的页面,点开某个job的配置,发现了个参数hive.input.format,值为org.apache.hadoop.hive.ql.io.CombineHiveInputFormat,建源表时,我没有指定自定义的format,我一直以为默认的是org.apache.hadoop.hive.ql.io.HiveInputFormat,难道是这个???另外有个hive.script.serde=org.apache.hadoop.hive.serde2.lazy.LazySimpleSerde,我定义源表时指定了row format SERDE 'org.apache.hadoop.hive.contrib.serde2.RegexSerDe',这两个又是什么关系?
去看CombineHiveInputFormat的源代码,找到getsplits方法还没读明白。。。。