偏激了一点.
总体来说Hive的思想是不错的, 思路是清晰的, 但代码也是啰嗦的, 简单的功能非得涉及到3,4个类,有时候十多个类。
1. 实现代码过量使用自己造的术语以及由它们引申的术语,导致代码理解起来非常困难, 例如SerDe(DynamicSerDe, LazySimpleSerDe), 如果说SerDe从字面Deserializer+Serializer还比较好理解的话. 那么RowResolver, ObjectInspector就无从知道了, 更严重的是这些关键类几乎没有文档说明, 而且这些类都有密切的关系.
还有一例: *Desc, *Work, *Task. 静下心来, 还是能理解的, 为了序列化成必须有像*Desc这样的东西, 但这些东西一多, 就会看到代码充斥着这些类的实例, 而且非常多代码去维护它们之间的关系.
2. thrift, thrift这东西本来就不是开源界所广泛接受的东西,当然facebook使用自己的东西也无可厚非. 但如果要把HiveServer做成多用户的Server, thrift行不行那还得考量. 我尝试过, 这种RPC做各进程交换数据, 相互调用还是非常方便的, 但是做Server不是很方便.
3. SessionState. 使用ThreadLocal将各会话的数据进行线程封闭, 造成了太多地方使用SessionState.get(). 像全局变量一样, 引入隐晦的类间耦合.
4. 细节. reduceSinkDesc里有两个tableDesc成员变量,乍一看让人不解. map输出操作的时候, 要table描述干嘛? 仔细一看, 其实只要用到它的序列化及反序列化功能. 在PlanUtils. getBinarySortableTableDesc()方法内大张旗鼓地向tableDesc灌入DynamicSerDe, SequenceFileInputFormat, SequenceFileOutputFormat...实际上ReduceSinkOperator只需要那个SerDe去解析key/value.
这个tableDesc很滑稽. groupby_key+ distinct_key形成了ExecMapper要输出的key, reducekey由SemanticAnalyzer. genGroupByPlanReduceSinkOperator()组合, 然后调用PlanUtils.getReduceSinkDesc()方法按照keyCols建立一个tableDesc, 它的ddl样子如下
struct binary_sortable_table
{
类型名 reducekey0,
类型名 reducekey1,
...
}
这个tableDesc开始是放在reduceSinkDesc中的, 用来给mapper序列化key, 但是reducer得到key要反序列化它啊, 否则怎么知道这个key的内容? 所以reducer也要得到这个tableDesc. 怎么得到呢? 在mapredWork里面有个tableDesc keyDesc, 在hdfs上建立plan.[0-9]+文件,reducer可以从这里得到。 但这个tableDesc是怎么传给mapredWork的呢? 本来它是在reduceSinkDesc里面啊. 仔细一找, SemanticAnalyzer.analyzeInteral的最后一步genMapRedTasks(qb) ->setKeyDescTaskTree(rootTask) ->GenMapRedUtils.setKeyAndValueDesc(work, op) -> ReduceSinkOperator rs = (ReduceSinkOperator)topOp; plan.setKeyDesc(rs.getConf().getKeySerializeInfo());
曲曲折折, 终于把这个tableDesc从reduceSinkDesc赋给了mapredWork, 终于reducer可以得到它了, 上帝都感动得哭了!
5. 细节. GenericUDF不错, 但initialize必须得返回非null实例, 否则hive会抛出NullPointerException而crash. 造成一下假设: 扩展人员知道这个情况, 但很多时候大家是不知道的.
GenericUDFUtils.ReturnObjectInspectorResolver. update(oi), 如果传进去的oi一直为null, 那它由get()返回的ObjectInspector必定为null, 从而initialize()返回null, 作者本人都不知道会有这种情况.
看GenericUDFHash的代码, initialize返回一个PrimitiveObjectInspectorFactory.writableIntObjectInspector作为trick, 骗骗其它的代码. 其实在evaluate的时候根本没用. 好的代码不应该给别人造成side impression(not effect here).
6. 看过代码,你会发现解析thrift文法(如DynamicSerDe类)用的是JavaCC做的lexer&parser, 而Hive的sql解析则用的是antlr生成的lexer&parser.也就是说,要掌握hive这个项目,你至少要会两种CC.
7. 细节. reduceSinkDesc有两个成员keyCols和partitionCols, 分别用来做为map输出的key和partition, 其实在group by的时候. partitionCols是reduceSinkDesc的一部分. 没有必要copy成两份, 这个partitionCols没有带来更灵活的partition功能.
未完待续