从2012-01-29开始提交到github开始算,现在phpHiveAdmin已经开发了接近两个月。很多是业余时间在写,上班也会写写,但主要工作还是维护hadoop集群,hive集群,跟各部门的数据需求进行对接和偶尔编写一些map/reduce脚本,其实map/reduce脚本写的已经少了,大部分都是我的同事在写。帮我分担了不少工作压力,很感谢他们,也很感谢我的上级,全力支持我做这么一个公司项目外的破玩意。我真觉得我们数据组的人都是最棒的,无论从人品还是技术实力来说。
公司里面已经将phpHiveAdmin内部版作为开放数据平台项目的一部分,在公司各部门推广使用了,以后数据组直接的任务压力会减轻不少。很多工作可以分流给各个部门自己查询使用了。
这两天,查询任务分流了,闲下来了,就记录一下这个开发过程和架构构思和想法心态,以后回顾起来也是个乐子。
当初开发phpHiveAdmin说起来也偶然,也必然。在第一次easyhadoop开发者聚会上轮到我讲解这个玩意,我是先这样跟大家介绍自己的:“我是个很懒的人。”懒其实是一种欲望,欲望是我写这个东西的主要动力。印象里,在以前淘宝数据所做的报告中,淘宝有50%以上的数据查询都是由hive完成的。最根本的原因,我认为,是hive满足了懒人的需要,比如我以前是写mysql的或者sql server的,或者oracle的,都可以很容易的上手转向使用hive,因为hive就是个SQL语言的查询器。我很懒,我不想再去学一个新的语言,pig,或者其他什么东西。那么hive拿过来稍微学一点点就可以快速的进行数据统计分析的工作,剩下的无非就是sql语言水平的差异了。另外,你教一个数据统计的新人用sql语言和教他用一个全新的语言,显然sql语言的时间精力成本更低。
基于懒的激励,我本来想找一个现成的开源来用,但搜遍了google,却发现没有hive最易配置和最易使用的开源周边工具,我和我的同事们不得不面对一个SSH命令行。每天重复的敲use xxxx;select xxxx, use xxxx;select xxxx, use xxxx; create xxxx;use xxxx; alter xxxx......无休无止,暗无天日。hwi那个破玩意又那么简陋和难以使用。而且数据分析的sql也都知道,一写就是十几二十行,看的头晕眼花。
为自己,保护视力,我决心改变这种窘境,自己写一个东西,核心目标就是简单,易用。没有那么多高尚的情操,什么为中华民族伟大复兴而努力编程之类的,完全没有,就是为了自己省事,一方面省得写sql语言了,一方面如果教会别人用,扔给别的部门自己查数,我连数据查询任务都不用接了。于是某天下班后自愿加班,写了三个小时,用thrift写出了一个简单的查询器。看能简单查询,就扔下回家了。
然后第二天我自己用的时候,立刻就遇到了第一个大问题:执行超时。
Hive由于是跑hadoop的map/reduce任务,所以通常查询时间会很长,这就带来一个web server超时和php进程执行超时的问题,改了相关的配置文件还是不行,就怀疑是thrift的问题,仔细看了看机器生成的源代码,读得这叫一个费劲,还上网找了各种资料。于是后来就有了我那篇
php开发hive web查询 的文章。
增加了一些添加删除数据库,表的功能,开始在数据组内部推广使用。并且整理了一下代码,开始开源。不过一开始的情况并不理想,同事们觉得这个东西很方便,但是又都不爱用,究其原因,是一个执行状态返回的问题。人的心理是个很微妙的东西,所谓众口难调,有些人喜欢盯着程序一步步执行,有些人喜欢扔在那就出去玩了,一会儿回来看结果。
不过有状态返回比没有强。这就引出了第二个大问题:非阻塞和异步执行。
这个问题其实也分成三个小问题,一个是从哪即时获取map/reduce执行状态,一个是如何非阻塞和异步执行,还有一个就是环境变量的问题。
第一个小问题其实就困扰了一段时间,在命令行cli方式下执行hive查询,看到的map/reduce进度和结果返回都是在一个屏幕输出的,但是实际在hive内部执行是分两个地方输出的,map/reduce是执行状态是输出到stderr,而结果集是输出到stdout。为了解决这个问题,又得阅读,文档是很难发现的,都是洋文,看也瞎看(其实我自认为洋文不差,侃个大山,骂骂脏话不成问题,但是看时间长了眼花),看了一下源码,又结合shell的重定向操作,才发现如何分离这两种输出。其实在这个问题上我就已经纠结了,这期间我曾经想放弃用php,想转用python或者java,shell结合起来开发。因为thrift是断然获取不到stderr的输出,php又无法非阻塞的实时获取shell脚本的输出流,所以就很头疼。玩过php的都知道,php的非阻塞和异步简直就是噩梦,我为了这事连python的tornado都架上了。但是最终还是攻克了这个难题,非阻塞和异步执行最核心思想就是多进程,幸好php里有proc_open,至于我为什么没用pcntl开发多进程,其实也是我为什么最终决定没用python或java开发异步的原因。
很简单,我就是想让这个东西最简化,让使用者的部署难度降到最低。因为我就是使用者,我不想给自己找麻烦!!你只要配上默认配置的php就可以用,php的configure完全不需要加任何--enable或者--with。如果你不要meta数据,你安装php甚至不需要配置mysql就可以用phpHiveAdmin!!
在解决这两个小问题的同时,意外的解决了一个bug,就是内存溢出的问题,thrift是直接从hive的端口读取数据,大多数情况下,hive返回的数据都会很大。thrift不管,一概全读,很轻松就超过了php内存的限制,造成内存溢出。转用多进程命令行方式以后,是stdout输出,如果出问题了,那他妈一定是Linux Kernel的问题了,就只能找Linus解决了。意外解决了这个问题,是个出乎意料的收获。
上面是前两个问题,也是最困难的部分。第三个问题不难,但是挺混蛋的。为了获取hive的map/reduce输出,我把查询部分改造成了命令行方式执行。但是这带来一个问题,任何执行都会报HADOOP和JAVA环境变量找不到。原因是这样,php的任何一个exec,或者system,或者passthru,或者proc_open。都相当于一个shell进程,或者相当于一个crt窗口,你用多个exec,其实就相当于开了多个SecureCRT或者putty的窗口,其实哪个之间的环境变量都跟其他的无关。所以尽管写了好几个exec,执行到hive的时候还是会报找不到环境变量。所以,没办法,只能全写在一行里了。这个是最简单的一个小问题了。
于是这几个问题都解决了,于是就有了里程碑版本,0.05 beta2。可以通过非阻塞和异步实时的查看map/reduce状态,且不会内存溢出。说到这儿我也要感谢一个做php页面开发的同事,他最近被土豆挖走了。我的前端水平很烂,非常烂,他帮我用ajax实现了页面的异步读取文件并刷新,所以才能实时的查看map/reduce的状态,我真的一点不会现在的页面技术,出去找工作肯定没人要。
phpHiveAdmin的ETL功能我自己觉得还是比较有趣的事情,用ini文件实现配置hive自动运算数据,并将结果导入mysql数据库,这是我目前能想到的最简单和最方便的ETL配置方法了。目前还没有着力研发,还属于试验阶段,但其实已经可以用了。未来计划会加入crontab版的ETL,那就齐活了,phpHiveAdmin调试HQL语句,然后放到ETL里面定时执行,完美的数据抽取和展现方案,我称之为一揽子计划,或者叫菜篮子计划也可以。
还有就是我为什么使用HQL自带的查询来获取数据库名,表名等等资料而不用metastore里面的数据。因为这是最省事的办法,而且无需关心hive的metastore用的是什么存储引擎。metastore目前我已知有两种存储方式,jdbc和odbc,用的最多可能就是mysql,pgsql和hive自带的derby了。mysql, postgreSQL都需要jdbc,derby需要unixODBC,如果我拿出去部署,没有jdbc,岂不就要在编译php的时候安装unixODBC吗?太麻烦了,我不能给自己找麻烦。况且,元数据太重要了,一旦随意使用被破坏了,一切都完蛋。所以我就没有用元数据来获取hive的数据库结构,表结构等信息,而都是用HQL来完成这些信息获取的。基于这种想法,也就没有使用hive自带的权限控制,而是自己写了一套权限控制,虽然现在还很简陋,我不希望任何复杂的东西成为phpHiveAdmin安装部署的一个羁绊。
目前phpHiveAdmin更新到了0.06 beta2,正在努力实现partition的所有相关功能。版本库在github,googlecode和sourceforge上都有同步更新,那个快访问那个。
同时也欢迎更多爱好开源的人加入到这个项目,共同成长,进步,直接给我发短消息就好了。
访问http://www.phphiveadmin.net获取更多信息。