惊呆,Oracle的这个坑竟然让我踩上了

数据库 Oracle
今天,系统中的一个业务处理莫名地执行了6个小时都没有结束,正常处理也就是3分钟左右,对原因进行定位,发现是在Oracle客户端上同步执行一个命令没有响应。今天来分享一下这个问题,让更多的人避开这个坑。

[[426800]]

今天,系统中的一个业务处理莫名地执行了6个小时都没有结束,正常处理也就是3分钟左右,对原因进行定位,发现是在Oracle客户端上同步执行一个命令没有响应。今天来分享一下这个问题,让更多的人避开这个坑。

1 业务场景

我们要把一个csv文件(文件名biz.csv)中的数据读取到Oracle数据库表(表名t_biz,t_biz)中,数据库表t_biz表结构如下:

biz.csv文件内容如下:

  1. id,a,b,c 
  2. 1,a1,b1,c1 
  3. 2,a2,b2,c2 
  4. 3,a3,b3,c3 

把biz.csv文件的内容读入到表t_biz,为了提高效率,这里使用了sqlldr 命令,命令如下:

  1. sqlldr test/test123@biz control=/home/jinjunzhu/biz/T_BIZ.ctl log=/home/jinjunzhu/biz/T_BIZ.log bad=/home/jinjunzhu/biz/T_BIZ.bad 

解释一下这个命令,test/test123 是要访问的数据库实例的用户名/密码,biz 是数据库实例名称。T_BIZ.ctl是控制文件,内容如下:

  1. options(skip=1,rows=10000,errors=0,parallel=true,bindsize=1048576,readsize=1048576) 
  2. load data  
  3. infile '/home/jinjunzhu/biz/biz.csv' 
  4. fields terminated by ',' 
  5. truncate into table day_data 
  6. trailing nullcols 
  7. (id,a,b,c) 

业务代码中调用这个命令,代码如下:

  1. private int execute(String cmd) throws Exception{ 
  2.     Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash""-c", cmd}); 
  3.     process.waitFor(10, TimeUnit.SECONDS); 
  4.     Integer status = process.waitFor(); 
  5.     return status == null ? -1 : status; 

2 问题现场

程序执行到上面第4行的时候,程序hang住了,一直没有返回。这个代码之前从来没有出过问题,最近也没有上过线,今天唯一的不同就是文件数据量越来越大,今天比昨天大了几万行。

数据库情况:

  • 看不到有sqlldr命令等待的情况
  • CPU正常
  • 手工执行上面命令可以成功,但是打印的日志非常多,如下图:

3 原因分析

网上搜这个问题竟然很多,原因有下面三类:

3.1 Oracle版本低

Oracle版本低,建议升级到10.2.0.2或以上,这个方案忽略,因为我们的数据库版本是Oracle 11.2.0.4.0。

3.2 数据落库情况

本以为sqlldr命令执行失败了,但是文件数据已经全部落到t_biz表。这说明命令执行成功了,只是Oracle没有给应用返回结果。难道是Oracle数据库hang住了?但是上面的问题现场已经确认,Oracle并没有hang在sqlldr这个命令上。

3.3 最终答案

看了好多博客,最后发现竟然不是Oracle的原因。根本原因是使用java执行shell时,如果不读取标准输出,这个输出就会输出到缺省缓冲区,如果输出流太大,必将打满缓冲区,导致程序hang住。

从上面问题现场的手工执行中可以看到,因为加载的数据量很大大,结果输出也流非常大,这很容易超出缺省缓冲区大小。

4 解决方案

问题已经很明确了,解决方案也就有了,处理sqlldr的输出就可以解决。解决方法有下面三种。

4.1 增加参数

在sqlldr命令后面增加一个参数,silent=(ALL),最后命令如下:

  1. sqlldr test/test123@biz control=/home/jinjunzhu/biz/T_BIZ.ctl log=/home/jinjunzhu/biz/T_BIZ.log bad=/home/jinjunzhu/biz/T_BIZ.bad silent=(ALL

4.2 程序读取标准输出

程序中读取sqlldr命令返回的输出,修改后的代码如下:

  1. private int execute(String cmd) throws Exception{ 
  2.     Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash""-c", cmd}); 
  3.     process.waitFor(10, TimeUnit.SECONDS); 
  4.     Integer status; 
  5.  
  6.     BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); 
  7.     String line; 
  8.     while ((line = br.readLine()) != null) { 
  9.         System.out.println(line); 
  10.     } 
  11.     return (status = process.waitFor()) == null ? -1 : status; 

4.3 文件接收标准输出

可以在sqlldr命令中增加文件参数来接收命令的标准输出,最后我采用了这种方式,命令如下:

  1. sqlldr test/test123@biz control=/home/jinjunzhu/biz/T_BIZ.ctl log=/home/jinjunzhu/biz/T_BIZ.log bad=/home/jinjunzhu/biz/T_BIZ.bad 1>/home/jinjunzhu/biz/std.log 2>/home/jinjunzhu/biz/err.log 

5 总结

这个问题刚出现的时候,一直以为是Oracle的问题,但是后来研究发现,这个锅真的不能让Oracle来背。关于sqlldr命令的详细参数介绍,已经比较成熟,大家可以自行网络查找。

【编辑推荐】

 

责任编辑:姜华 来源: 程序员jinjunzhu
相关推荐

2021-02-09 09:50:21

SQLOracle应用

2020-03-12 15:00:44

JavaSpring依赖

2022-01-03 20:13:08

Gointerface 面试

2018-07-06 05:05:07

2020-06-01 14:02:25

Vue.js框架模板

2021-02-24 09:43:36

MySQL数据库双引号

2020-04-02 07:31:53

RPC超时服务端

2022-07-26 01:00:12

Eureka延迟注册

2022-06-24 14:52:34

AI模型

2021-07-05 18:05:40

SpringBean方法

2015-05-14 12:41:45

智能

2021-06-29 10:02:04

亚马逊机器解雇

2020-11-03 06:57:10

MyBatis数据库

2019-09-18 15:20:16

MyBatisSQL数据库

2018-10-15 15:42:57

数字化企业转型

2021-04-16 07:04:53

SQLOracle故障

2021-01-01 09:03:44

故障HAProxy服务器

2020-07-17 11:30:39

密钥Github数据安全

2020-01-06 09:14:59

Java程序员线程

2023-11-23 16:46:55

LinuxAWK运维
点赞
收藏

51CTO技术栈公众号