你真的了解Lateral View explode吗?--源码复盘

​这几天情绪真的很down啊,包括写这篇文章的时候,无论如何,希望大家看了这篇文章后,功力能再上涨一层~~

image

用Lateral view explode这么久,竟然发现,不是很了解它?

Lateral view与UDTF函数一起使用,UDTF对每个输入行产生0或者多个输出行。Lateral view首先在基表的每个输入行应用UDTF,然后连接结果输出行与输入行组成拥有指定表别名的虚拟表。

UDTF函数需要继承GenericUDTF.java,在hive源码中,可以查到有以下8种UDTF函数:

image

我们最常用的就是explode了,可是如果面试的时候,我问你:Lateral view explode 会产生shuffle吗?为什么会,或者不会?

你确定你能毫不犹豫、确定无疑的答出来吗?

如果不能,那你真的需要看看这篇原理复盘的文章。

源码分析

该从哪里说起呢?写一个简单的sql:

explain SELECT id, sq,myCol from window_test_table LATERAL VIEW explode(split(sq,',')) myTab as myCol;

这个sql不多说,一眼看透,下面看一下执行计划:

image

上面我把执行计划的 执行顺序给标注了一下,说实话,真的不太喜欢hive的执行计划,太长了,相反 spark的就很简洁,看一眼就知道怎么回事,但很多时候,还是喜欢来分解hive的实现过程,哈哈,这是不是找虐型的~~

看了我的标注后,一目了然, 这个sql 经历了两条线:

ts(TableScan)-->lvf(Lateral View Forward)-->sel(Select)-->lvj(Lateral View Join)-->sel(Select)
ts(TableScan)-->lvf(Lateral View Forward)-->sel(Select)-->udtf-->lvj(Lateral View Join)-->sel(Select)
image

1、TableScanOperator

不多说,常规读表操作

2、LateralViewForwardOperator

@Override
public void process(Object row, int tag) throws HiveException {
  forward(row, inputObjInspectors[tag]);
}

几乎什么都没做,数据怎么来的,还怎么送出去。

它的作用就是告知一下:

image

2-1、左侧SelectOperator

筛选出你需要的非explode的列:id,sq

2-2-1、右侧SelectOperator

筛选出explode的列:split(sq, ',')

2-2-2、右侧UDTFOperator

这个稍显复杂,代码里暗藏玄机

@Override
public void process(Object row, int tag) throws HiveException {
  StructObjectInspector soi = (StructObjectInspector) inputObjInspectors[tag];
  List<? extends StructField> fields = soi.getAllStructFieldRefs();
  //从row里解出字段
  for (int i = 0; i < fields.size(); i++) {
    objToSendToUDTF[i] = soi.getStructFieldData(row, fields.get(i));
  }
​
​
//真正处理数据的是 genericUDTF的某个实现类,比如,explode,那就是GenericUDTFExplode.java 的process
  genericUDTF.process(objToSendToUDTF);
  //这里判断一下有没有outer关键字。这里真的真的真的是,可能用了很久了,还不知道udtf还有个outer 关键字
  if (conf.isOuterLV() && collector.getCounter() == 0) {
    //思考一下这一步是干嘛?
    collector.collect(outerObj);
  }
  collector.reset();
}

GenericUDTFExplode.java就相当容易理解了,毕竟我们自己写udtf时,也是这么做的:

/**
 * GenericUDTFExplode.
 *
 */
@Description(name = "explode",
    value = "_FUNC_(a) - separates the elements of array a into multiple rows,"
      + " or the elements of a map into multiple rows and columns ")
public class GenericUDTFExplode extends GenericUDTF {
....
@Override
//主要处理数据的方法
public void process(Object[] o) throws HiveException {
  switch (inputOI.getCategory()) {
  case LIST:  //处理list
    ListObjectInspector listOI = (ListObjectInspector)inputOI;
    List<?> list = listOI.getList(o[0]);
    if (list == null) {
      return;  //当数组里没有值时,不发送数据
    }
    for (Object r : list) {
      forwardListObj[0] = r;
      forward(forwardListObj);
    }
    break;
  case MAP: //处理map
    MapObjectInspector mapOI = (MapObjectInspector)inputOI;
    Map<?,?> map = mapOI.getMap(o[0]);
    if (map == null) {
      return;
    }
    for (Entry<?,?> r : map.entrySet()) {
      forwardMapObj[0] = r.getKey();
      forwardMapObj[1] = r.getValue();
      forward(forwardMapObj);
    }
    break;
  default:
    throw new TaskExecutionException("explode() can only operate on an array or a map");
  }
}
....
}

上面所有的,都没有什么特别的,如果必须让找一个的话,那我选择outer关键字吧。

为什么要有outer关键字?

当UDTF不产生任何行时,比如explode()函数的输入列为空,LATERALVIEW就不会生成任何输出行。在这种情况下原有行永远不会出现在结果中。OUTRE可被用于阻止这种情况,输出行中来自UDTF的列将被设置为NULL。

看下图结果便一目了然:

image

实际上从代码里,也能够看到:

image

当没有值时,是return掉,不会forward,如果不forward的话,那这条数据就不会被传入下个Operator,也就不会被输出

那outer是怎么处理的呢?

image

UDTF会借助UDTFCollector为其展开的结果计数,并forward:

@Override
public void collect(Object input) throws HiveException {
  op.forwardUDTFOutput(input);
  counter++;
}

如果没有展开结果,counter就为0。这样,进入outer之后,会把之前建好的没有内容的outerObj给forward到下个 算子LateralViewJoinOperator

3、LateralViewJoinOperator

@Override
public void process(Object row, int tag) throws HiveException {
  StructObjectInspector soi = (StructObjectInspector) inputObjInspectors[tag];
//标识是左侧select过来的  
if (tag == SELECT_TAG) {
    selectObjs.clear();
    selectObjs.addAll(soi.getStructFieldsDataAsList(row));
  } else if (tag == UDTF_TAG) {  //代表是右侧udtf过来的
    acc.clear();
    acc.addAll(selectObjs);
    acc.addAll(soi.getStructFieldsDataAsList(row));  //合并数据
    forward(acc, outputObjInspector);
  } else {
    throw new HiveException("Invalid tag");
  }
​
​
}

LateralViewJoinOperator处理逻辑也是很简单明了,这里的join也是简单的List.addAll

最后最后

那问题来了?

Lateral view explode 会产生shuffle吗?

当然不会,毋庸置疑!其实一开始看执行计划就会发现,没有reduce任务呀~~

这里的Join代表的是两份数据联接到一起的意思,并不是真正的意义上的join。

另外,还有一点:

大家要注意 加outer和不加outer的区别,在日常工作一定要注意,切记切记!

不要丢了数据,还不知道为啥

最后最后最后

再考虑一下,在我们看来 Lateral view explode 仅仅是个很简单的 数组 or map 结构展开,再联接的操作,几行代码就能搞定的事, 为何,hive要设计的这么麻烦呢?

毋庸置疑!-- 为了解耦。

UDTF 可以单独用,可以和Lateral view一起用,并且用户还可以定制自己的UDTF~~

这一切 都与hive这样灵活的设计分不开的

Hey!

我是小萝卜算子

欢迎扫码关注公众号

image

在成为最厉害最厉害最厉害的道路上

很高兴认识你

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342