MapReduce中使用字符串拼接导致的问题,mapreduce字符串


今天在帮业务方优化mapreduce作业时遇到一个用字符串拼接的问题。

reduce中字符串拼接代码:

while (it.hasNext()) {
      line = it.next().toString();
      artID+=line+",";
      ct++;
}

采用的是String直接拼接,结果是该作业作业只剩一个reduce一直在运行,而且时间长达6小时,其他已经完成的reduce只用了10多分钟。

咨询了一下业务方,artID有的会很长,这个时候会一直进行字符串拼接。

我又看了一下未完成和已完成的reduce处理的数据量差别不大,未完成的需要处理60M的数据,已完成的处理了50M的数据。

1、查看该reduce的java进程的堆内存使用情况,不存在堆内存不够用,导致一直FGC的情况。

2、查看该reduce的java进程的cpu使用情况:

[mapred@yz5102 ~]$ top -p 39257
top - 14:48:37 up 393 days, 20:34,  2 users,  load average: 8.75, 15.84, 15.53
Tasks:  43 total,   1 running,  42 sleeping,   0 stopped,   0 zombie
Cpu(s): 28.8%us,  7.2%sy,  0.0%ni, 61.7%id,  0.9%wa,  0.0%hi,  1.3%si,  0.0%st
Mem:  49374704k total, 41905644k used,  7469060k free,  2167520k buffers
Swap:  8388600k total,   341168k used,  8047432k free, 27925176k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
39258 mapred    20   0 2432m 1.6g  15m R 94.1  3.4 303:47.63 java
39263 mapred    20   0 2432m 1.6g  15m S  5.7  3.4  11:31.72 java
39265 mapred    20   0 2432m 1.6g  15m S  5.3  3.4  11:32.03 java
39268 mapred    20   0 2432m 1.6g  15m S  5.3  3.4  11:34.02 java
39271 mapred    20   0 2432m 1.6g  15m S  5.3  3.4  11:32.73 java

发现39258这个线程的cpu使用率很高。

打印出该reduce的java进程的线程堆栈信息并找到39258线程相关的信息,39258的十六进制为0x995a,找到nid=0x995a的线程就可以了:

"main" prio=10 tid=0x00007f36e000d000 nid=0x995a runnable [0x00007f36e6ab6000]
   java.lang.Thread.State: RUNNABLE
        at java.util.Arrays.copyOfRange(Arrays.java:3209)
        at java.lang.String.<init>(String.java:215)
        at java.lang.StringBuilder.toString(StringBuilder.java:430)
        at weMedia.create.ArticleInfoReduce.reduce(ArticleInfoReduce.java:23)
        at weMedia.create.ArticleInfoReduce.reduce(ArticleInfoReduce.java:13)
        at org.apache.hadoop.mapreduce.Reducer.run(Reducer.java:171)
        at org.apache.hadoop.mapred.ReduceTask.runNewReducer(ReduceTask.java:627)
        at org.apache.hadoop.mapred.ReduceTask.run(ReduceTask.java:389)
        at org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:167)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:396)
        at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1550)
        at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:162)

线程堆栈信息显示,该线程cpu使用率高是因为一直在进行字符串拼接,虽然代码直接使用的是String,但是JVM底层转化成了StringBuilder的操作,按理说转换成了StringBuilder效果不应该低,但是实际结果并不理想。

最后让业务方显示的修改了字符串拼接部分的代码,使用StringBuffer,重新执行了一下该作业,作业全部运行完成只用了10多分钟,和修改之前的6个小时,运行效率提高了很多倍。

小结:

虽然直接用String拼接,底层实际使用的是StringBuilder进行操作,但是效率还是不尽人意,最好还是显示的用StringBuilder或者StringBuffer吧,不要寄希望于底层转换。





相关内容