Serde是什么:Serde实现数据序列化和反序列化以及提供一个辅助类ObjectInspector帮助使用者访问须要序列化或者反序列化的对象。app
Serde层构建在数据存储和执行引擎之间,实现数据存储+中间数据存储和执行引擎的解耦。框架
//主要实现数据的序列化和反序列化。oop
publicabstractclass AbstractSerDe implements SerDespa
{翻译
publicabstract Writable serialize(Object obj, ObjectInspector objInspector)3d
throws SerDeException;orm
publicabstract Object deserialize(Writable blob) throws SerDeException;对象
publicabstract ObjectInspector getObjectInspector() throws SerDeException;blog
}hadoop
这里为何提到数据存储和中间数据存储两个概念,由于数据序列化和反序列化不单单用在对目标文件的读取和结果数据写入,还须要实现中间结果保存和传输,hive最终会将SQL转化为mapreduce程序,而mapreduce程序须要读取原始数据,并将最终的结果数据写入存储介质,Serde一方面用在针对inputformat中RecordReader读取数据的解析和最终结果的保存,另外一方面,在map和reduce之间有一层shuffle,中间结果由hadoop完成shuffle后也须要读取并反序列化成内部的object,这个object实际上一般是一个Array或者list,但hive会提供一个StandardStructObjectInspector给用户进行该Object的访问。
Hive翻译SQL以后会产生不少operator,一个Operator产生的数据须要由下一个operator继续处理。
咱们先看一个具体案例:select SUM(show),SUM(click) FROM TableA GROUY BY province,其最终被翻译成operator执行树结果以下图。
上一个Operator的输出是下一个Operator的输入,每个Operator处理后,数据已经发生变化,好比SelectOperator会将用户没有选择的列去掉(若是数据读取端没有按列存储,通常将读取全部列数据上来),再好比通过groupby算子(map阶段被下推的group by)的数据,key已经变为聚类的列,value已经变成局部聚类的结果,因此每个operator必须知道上一个operator结果数据的格式,咱们先看下operator对象的一个重要方法:
void forward(Object row, ObjectInspector rowInspector);其实rowInspector这个参数已经没用了。
每个operator处理完数据后就经过该方法将结果推给下一个operator,可是针对ReduceSinkOperator是个例外,它须要把中间结果序列化交给hadoop框架进行数据shuffle,在reduce时,再反序列化回来(经过上文提到的Serde),继续遍历Operator树进行处理。
为何说这里的rowInspector参数已经没用了呢,咱们先看ExecMapper的configure方法,ExecMapper是惟一一个map实现类,针对reducer还有一个ExecReducer:这里只列出了相关内容的代码。
public void configure(JobConf job)
{
//实例化一个MapOperator做为map执行的起点,保存一些上下文对象
mo = new MapOperator();
//这里的初始化方法其实调用的是Operator的初始化方法,最终调用到了Operator的initializeOp方法。
mo.initialize(jc, null);
}
每一个Operator在initializeOp方法中实例化本身的outputObjInspector对象,由于只有本身知道本身须要产生的数据是什么结构,并将该outputObjInspector最为参数调用本身childOperator的initialize(Configuration hconf, ObjectInspector inputOI, int parentId)方法,这样本身的childOperator就拿到了上一个Operator生成结果对象的ObjectOperator,也就是说,上一个Operator把数据对象推下来后,本身已经知道这个对象是什么结构了,至此完成了整个operator树的初始化。
那究竟这个ObjectInpector长什么样子,它如何帮助咱们访问实际数据对象呢:
publicinterface ObjectInspector
{
publicstaticenum Category {
PRIMITIVE, LIST, MAP, STRUCT, UNION
};
String getTypeName();
Category getCategory();
}
这里面应用最多的应该就是StandardStructObjectInspector。
经过getCategory方法能够知道该ObjectInspector是什么类型,咱们经过一个实例了解该对象的使用,经过ObjectInspector帮助咱们序列化对象:
staticvoid serialize(OutputByteBuffer buffer, Object o, ObjectInspector oi,
boolean invert) {
switch (oi.getCategory()) {
casePRIMITIVE: {
PrimitiveObjectInspector poi = (PrimitiveObjectInspector) oi;
switch (poi.getPrimitiveCategory()) {
caseVOID: {
return;
}
//若是该对象为布尔类型,直接经过BooleanObjectInspector对象的get方法读取布尔值,保存到buffer中。
caseBOOLEAN: {
boolean v = ((BooleanObjectInspector) poi).get(o);
buffer.write((byte) (v ? 2 : 1), invert);
return;
}
//若是该对象为byte类型,直接经过ByteObjectInspector对象的get方法读取byte值,保存到buffer中。
caseBYTE: {
ByteObjectInspector boi = (ByteObjectInspector) poi;
byte v = boi.get(o);
buffer.write((byte) (v ^ 0x80), invert);
return;
}
//Struct对象较为麻烦一些,若是是Struct对象,获取struct的全部属性和属性对应的ObjectInspector,逐个将每一个属性序列化到buffer中。
caseSTRUCT: {
StructObjectInspector soi = (StructObjectInspector) oi;
List<? extends StructField> fields = soi.getAllStructFieldRefs();
for (int i = 0; i < fields.size(); i++) {
serialize(buffer, soi.getStructFieldData(o, fields.get(i)), fields.get(
i).getFieldObjectInspector(), invert);
}
return;
}
}
Hive 中operator处理的数据都是Object加上一个ObjectInspector对象,咱们能够很是方便的经过该ObjectInspector对象了解到上游传过来的值是什么,若是是Struct对象,能够进一步了解到有多少了Filed,进而获取每一个Filed的值,并且经过Serde能够方便的完成数据的序列化操做。