记一则罕见的hive字段值异常引起map阶段的OOM

前段时间遇到了一个很诡异的发生的Map阶段的OOM异常,花了些时间才找到原因,这个简要记录一下。

先看log。

节点一的TaskTracker的log:

节点二的TaskTracker的log:

节点三的TaskTracker的log:

其他节点的TaskTracker中的log都和slave4的一样的:

故障分析:

OOM是一个比较常见的故障,其中发生在reduce阶段最为常见,最有可能是数据通过map函数后,shuffle到reduce端处理时由于分布问题导致某个分组的值太多出现OOM。另外,OOM也可能出现在DataNode进程上,这个属于比较罕见的情况,这个情况我就遇到过,是因为系统进程数配置的限制导致OOM的,具体情况可以看我这篇blog:http://zengzhaozheng.blog.51cto.com/8219051/1370125。对于上面的这个出现在Map阶段的OOM比较奇怪,一般在Map阶段不会出现OOM,因为一个Map task只处理一个split的数据的,而我们集群的block size只是配到64MB,照道理一个split size的大小应该为64MB左右(具体split size大小计算请看:http://zengzhaozheng.blog.51cto.com/8219051/1347777),从这点看来Map阶段不会出现OOM情况。从另外一方面看,Map阶段出现OOM的唯一地方就是通过io.sort.mb参数所控制的环形缓冲区当中,其对用的是Sort和spill环节,集群中环形缓冲区配到了500MB,整个虚拟机内存mapred.map.child.java.opts也配到了2G,照道理不会出现OOM。另外,为了安全起见还是通过改变这个2个参数去测测,结果还是意料之中出现OOM。

再回过头来看看日志,不同节点的TaskTracker的日志都有所不同,其中的节点一、二、三的共同点就是heap OOM和Buffer的读写错误,其他节点则都是报了一些文件错误。那么整个OOM错误的日志被归为2类,其中其他节点则都是报了一些文件错误很可能是由于节点一、二、三出现OOM引起的,具体原因还是要根据堆栈信息追一下源码,找到TaskLog类的createForWrite方法:

/**
 * Open the specified File for write access, ensuring that it does not exist.
 * @param f the file that we want to create
 * @param permissions we want to have on the file (if security is enabled)
 *
 * @throws AlreadyExistsException if the file already exists
 * @throws IOException if any other error occurred
 */
public static FileOutputStream createForWrite(File f, int permissions)
throws IOException {
  if (skipSecurity) {
    return insecureCreateForWrite(f, permissions);
  } else {
    // Use the native wrapper around open(2)
    try {
      FileDescriptor fd = NativeIO.open(f.getAbsolutePath(),
        NativeIO.O_WRONLY | NativeIO.O_CREAT | NativeIO.O_EXCL,
        permissions);
      return new FileOutputStream(fd);
    } catch (NativeIOException nioe) {
      if (nioe.getErrno() == Errno.EEXIST) {
        throw new AlreadyExistsException(nioe);
      }
      throw nioe;
    }
  }
}

从这段源码我们可以看出TaskTracker在创建TaskLog的时候出现了错误,其原因是TaskLog句柄已经存在了。呵呵,这个不难理解,当Map Task出现OOM之后,根据MapReduce的容错机制,其所在的TaskTracker进程会尝试重新启动的Map Task并且为其创建相同的TaskLog的文件句柄,但是由于出现OOM程序异常终止了并且之前的TaskLog文件句柄已经创建过但是没有被正常关闭,所以在重启启动Map Task是会检测到已经存在该句柄,所以报这个错。到这里第二类错误日志信息原因已经被解决,接下来集中精力看这个OOM错误原因。

这种情况的Map阶段出现的OOM之前没有遇到过,一时没有什么头绪,回过头来看看写的HiveSql脚本,功能很简单就是统计一个省当天网关日志的上网uv,对号码字段(mdn)做了一个count distinct最后group by了一下,当天日志做完ETL后才300多G左右,300G多的数据不算大。再看了看节点一、二报的这2个OOM信息,发现有IO读写错误的信息,主要是BufferedReader读取和用StreamDecoder解码出错,这下子想到会不会是mdn字段值有问题,出现乱码什么的导致读写错误最终引起OOM呢?试验一把!!首先检查mdn长度,distinct length(mdn)看看,照道理应该只有一个值并且这个值为11才对因为号码是固定的13位数,令人惊奇的是出现了3个不同的值,分别为11、16和18!在HiveSql中加上where length(mdn)=11,将长度为16和18的排除掉,重新执行HiveSql相当正常一点错误都没有!!查看号码mdn长度为16和18的究竟长什么模样,分别执行select * from tb_cdr where day=‘20140403‘ and length(mdn)=16 limit 5 和 select * from tb_cdr where day=‘20140403‘ and length(mdn)=18 limit 5,发现记录中有些乱码,这下子搞明白了。Map Task运行的时候数据流中包含了非法字符例如:EOF、NOP等东西,导致BufferedReader读取和StreamDecoder解码出错,正如节点一、二日志中看到的,进一步导致OOM。

总结:

对于这个OOM故障,按照一般的MapReduce框架运行原理思维和平常的OOM故障处理方式是很难揪出问题所在的。解决这样问题的关键点在于日志中的细小信息点,如BufferedReader读取和StreamDecoder的出错点,你不能够一看到OOM就不往日志下面看了,这种细小信息可以引导你到一个正确的方向,不至于漫无目的的瞎搞。另外,搞hadoop方面的技术人员最好是要精通于写MapReduce程序,我觉得这个很重,会写MapReduce程序那么你对MapReduce的整体处理流程一定会很清楚,出现问题能够比较容易找出原因。如果只会HiveSql,那么出现简单的错误自己都无法下手搞的,甚至连简单的数据倾斜原理都不知道,这样你写的HiveSql性能也许很低效的,所以一切都需要回到本质问题上来,hive的本质就是将HiveSql翻译成MapReduce程序。


记一则罕见的hive字段值异常引起map阶段的OOM,布布扣,bubuko.com

时间: 2025-01-02 16:46:47

记一则罕见的hive字段值异常引起map阶段的OOM的相关文章

因DataTable的字段值为DBNull引发的异常

1 问题重现 (1)新建项目DBNullExp,项目属性为"控制台应用程序": (2)在项目下新建数据集Schools(数据集文件的后缀名为.xsd): (3)在数据集下新建数据表Students,表字段的定义如下表所示: 字段名 说明 ID dc.DataType = Type.GetType("System.Int32");//类型 dc.AutoIncrement = true;//自动增量 dc.AutoIncrementSeed = 1;//起始为1 dc

sql语句之表间字段值复制遇到的一些问题--基于mysql

好久没来园子了,转眼2017已经到3月份了,前段时间一直忙没时间写博客(其实是自己懒),感觉内心好惭愧.昨天临下班前,技术老大突然对我说要改下表结构,问我能不能实现将一个表的字段值复制到另外一个表的某个字段中去,感觉这好拗口,其实就是表间字段值复制.于是,昨晚加了会儿班百度了下然后自己在本地测试了下,还真弄出来了,下面就把这个sql语句记下来,以备忘. 1,背景和需求 两张表a_user和b_user结构如下: a_user +--------+-------------+------+----

oracle的column格式化导致字段值显示为"####"的处理办法

问题: 查询某一个字段,结果字段值显示全部是"####" 原因: 之前在做其他查询的时候,对同样的字段名做了格式化:col value format a20,所以导致该列数据显示异常: 解决办法: 1.退出当前会话,重新登录 exit sqlplus /nologin conn /as sysdba 2.格式化清除:column 字段名 clear 即:col value clear 后来,经仔细查询,出错的这个字段本身是number数据类型,而之前做查询时候格式化的同名字段是varc

[Java] ES获取数据部分字段值

在ES中如何获取部分字段值,在默认情况下,ES搜索出来是source的全部字段,但有时候我们并不想获取全部字段数据,比如在开发中,我们的index中有几十个字段,每天好几十G的数据,全部返回量太大. 要获取指定字段的数据有两种方式, 1.使用默认方式查找出source所有数据,然后根据指定field进行过滤重组数据. 2.使用addField(fields)方式请求数据,然后利用getFields方式获取结果数据. 这里列出第二种方式的获取数据方式. //设置请求 SearchResponse 

使用LINQ获取List列表中的某个字段值

使用LINQ获取列表中的某个字段值,下面以获取员工列表中的编号字段为例子. 1.使用Select方法 1 List<Emplayee> emplayeeList = GetEmplayeeList(); //获取员工信息列表 2 int[] empIds = emplayeeList.Select(a => a.Id).ToArray(); //获取员工编号数组 2.使用ForEach方法 1 List<Emplayee> emplayeeList = GetEmplayee

C#三种判断数据库中取出的字段值是否为空(NULL) 的方法

最近操作数据库,需要判断返回的字段值是否为空,在网上收集了3种方法供大家参考 1 通过System.DBNull判断,网上大部分都使用这个方法. 复制代码代码如下: DataTable dt;                               //假设字段为name, dt已经保存了数据dt.rows[0]["name"] == System.DBNull.Value;  //判断第一行数据的name字段是否为空 2 通过IsNull判断 复制代码代码如下: DataTabl

GridView控件RowDataBound事件中获取列字段值的几种途径 !!!

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { e.Row.Attributes.Add("onclick", "javascript:alert('当前ID为:" +DataBinder.Eval(e.Row.DataItem,"CID"

速卖通---发布商品aeopAeProductPropertys这个字段值报07004013的错误

由于文档的说明很少,导致里面改填写那些值都是靠自己推敲出来,当然可以根据他们的错误提示了研究,他们的错误提示也给出了相关的帮助了, 例如通过categoryid的200000001获取到"id":10,"values":[{"id":200002203,"names":{"zh":"醋酸纤维","en":"Acetate"},"attri

update 表名 set 字段=值,数据更新

--数据更新 必须考虑是否有条件,往往能够做条件首先考虑主键值和唯一键语法:update 表名 set 字段=值,字段=值 .....where 条件 not|and|or--修改年龄 update Teacher set Age=18--将所有人年龄+1 设置表达式update Teacher set Age=Age+1--有条件的修改:修改张三的工资+1000,同时修改性别为女update Teacher set Salary+=1000,Gender='女' where Id=4--修改出