1问题
生产环境一个批量处理没有完成。
2分析
批量处理逻辑:
java->shell->sqlldr
检查数据库会话,发现对应会话等待事件为SQL*Net message from client,对应v$session.program为sqlldr@xxx,v$session.seq#保持不变,持续超过5个小时(6:45-12:20)。
查询数据库加载的表,发现已加载部分数据,但对应sqlldr日志为空。
对应sqlldr命令如下:
sqlldr user/pwd data=a.txt control=a.ctl log=a.log bad=a.bad discard=a.dis errors=9999999 rows=1000
在测试环境验证,sqlldr直接运行时,可以顺利加载所有数据;通过java->shell->sqlldr方式运行时,在加载特定行数后停止,问题可以重现。
在进行以上测试时,sqlldr直接运行时,当前窗口会输出已加载了xx行。其实,问题正出在这里。通过java->shell->sqlldr方式运行时,标准输出没有程序读取,在要加载的数据量达到一定程度时(超过缺省缓冲区大小),就会导致加载过程停止。
验证过程参考附录。
3解决方案
处理sqlldr的标准输出与错误输出,可选方法:
方法一: sqlldr ... silent=(ALL)
方法二: sqlldr .... 1>std.log 2>err.log
附,测试脚本
--RunShell.java
import java.util.Date;
import java.text.SimpleDateFormat;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class RunShell {
public static void main(String[] args){
try {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String shpath="/home/oracle/java/test.sh";
System.out.println("---1--" + df.format(new Date()));
Process ps = Runtime.getRuntime().exec(shpath);
System.out.println("---2--" + df.format(new Date()));
//ps.waitFor();
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
String loop = args[0];
System.out.println(loop);
String line;
while ("0".equals(loop) && (line = br.readLine()) != null) {
System.out.println(line);
}
System.out.println("---3--" + df.format(new Date()));
ps.waitFor();
System.out.println("---4--" + df.format(new Date()));
}
catch (Exception e) {
e.printStackTrace();
}
}
}
--test.sh
seq -w 1 100000|awk '{print $0"xxxxxxxxxxxxxx"}'
测试1--传入参数0,主线程主动读取shell的标准输出
java RunShell 0
---1--2018-04-20 13:23:25
---2--2018-04-20 13:23:25
0
...
...
---3--2018-04-20 13:23:34
---4--2018-04-20 13:23:34
==〉可以顺利完成
测试2--传入参数1,主线程不读取shell的标准输出
java RunShell 1
---1--2018-04-20 13:28:30
---2--2018-04-20 13:28:30
1
---3--2018-04-20 13:28:30
==〉长时间挂起
Ctrl-C
^C---4--2018-04-20 16:37:08
新闻名称:sqlldr标准输出未处理导致批处理挂起问题
新闻来源:
http://csdahua.cn/article/gceoeg.html