防止Oracle长任务被卡住方法

一、背景

  客户生产环境数据量比较大的时候,有一个存储过程作业需要执行22min左右,这个作业经常出现数据库作业已经执行完毕,而大数据平台还显示正在执行中的问题。导致整个业务流程无法顺利完成。

二、原因探究

2.1 原因分析

  由于oracle的存储过程使用的是jdbc的statement.execute()方法,该方法是同步操作,等待服务端返回结果,中间tcp连接不会用数据包传输。一些特定环境,防火墙会自动断开长期无活动的 TCP 连接,tcp-keepalive 可以在连接无活动一段时间后,发送一个空 ack,使 TCP 连接不会被防火墙关闭。因此,初步怀疑是防火墙把长时间没有数据交互的tcp连接给关掉了。

   重新执行客户的该作业,发现tcp链接计时逐渐递减,直至结束。截图如下:

  

因此,很有可能该链接被防火墙当成了僵尸链接断开了;而oralce的驱动却没有感知到。之前业务部门的同事也碰到过类似问题,发现设置hs.druid.keepAlive=true后链接就可用了。于是查了下oracle的驱动有没有类似的参数,并进行验证。

2.2 准备验证条件

1、修改代码,设置oracle驱动的keeplive参数,使该连接保持活跃。

需要修改代码,设置CONNECTION_PROPERTY_NET_KEEPALIVE参数,oracle参数具体使用事宜,可以参考该连接:

https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_TCP_KEEPIDLE

修改截图如下:

 

 2、DBA修改oralce数据库

  DBA修改oracle数据,可以设置sleep功能。编辑测试存储过程:

 

3、修改linux系统参数

修改linux的内核参数tcp_keepalive_time由7200(2h)改为60s,验证下60s后,tcp是否会发送一个0kb的空包使没有数据交互的connect的链接保持活跃。

  设置命令:

  echo 60 > /proc/sys/net/ipv4/tcp_keepalive_time

  查看设置结果:

  cat  /proc/sys/net/ipv4/tcp_keepalive_time

4、使用大数据平台触发存储过程

  创建调用存储过程作业

 

 

 

 

5、监控tcp报文发送

  • 使用ps –ef |grep compute 查看离线计算进程号

  • 使用netstat命令查看tcp链接:netstat –anp|grep 22886

 

 

 

  •  执行存储过程,监控tcp链接

 

由于链接的是192.168.86.229的服务器,因此可以看到最后一个就是需要检测的tcp链接

 

 

 

  • 使用客户端进行监控

进入到/proc/net目录

cd  /proc/net

cat /proc/net/nf_conntrack | grep 54490

 

1分钟后,可以看到tcp剩余时间又恢复了,说明监控生效了

 

  • 服务端监控

或者在oracle所在的服务器使用:tcpdump –I ens160 port 1521 and host xxx的方式监控源端发送过来的报文。监控示意图如下。

 

2.3 分析验证结果

使用调度重新调用客户作业,发现oracle的作业能正常执行了。正常执行的截图示例如下:

 

 

  将参数tcp_keepalive_time调整为7200s,再次执行作业,发现仍然执行失败。失败截图如下:

 

 

说明确实是因为防火墙的原因,把长时间处于等待没有进行数据交互的tcp给杀掉了。但是如果没有设置CONNECTION_PROPERTY_NET_KEEPALIVE参数的话,oralce驱动是感受不到tcp断链的,一直在傻傻的等待。

2.4 修改应用程序tcp_keepalive_time

 1、修改oracle的tcp_keepalive_time

  为了确保能不修改客户服务器的参数配置便能解决掉该问题,查找了一下oralce官方的说明,发现oracle.net.TCP_KEEPIDLE这个参数是用来设置tcp_keepalive_time参数用的,但是在oracle的12.1.0.2设置完之后不生效,经过查询oracle的官方说明,才知道该参数从21版本才开始支持,对应的常量是CONNECTION_PROPERTY_TCP_KEEPIDLE。链接如下:

https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_TCP_KEEPIDLE

 

  2、升级oralce的驱动为 21.1.0.0

 升级oralce驱动之后,再次调用存储过程,发现1.8版本的JVM又不支持该参数设置。错误截图如下:

 

3、升级JDK的版本

   通过查找oralce的官方说明,发现改参数设置需要jdk11以上版本才能支持,于是将jdk升级到11版本。具体链接如下:

https://docs.oracle.com/en/java/javase/11/docs/api/jdk.net/jdk/net/ExtendedSocketOptions.html

 

  在公司环境验证后,终于可以正常执行了。

 

 

 

三、总结

 经过以上验证,发现在一些特定环境下,防火墙为了定时清理一些僵尸链接,确实会自动断开一些长期没有数据传输的 TCP 连接;通过设置tcp-keepalive参数可以确保在一个连接一段时间没有数据传输时,发送一个空 ack包,使 该TCP 连接不会被当做一个僵尸链接被防火墙关闭。因此,解决该问题的方案有如下两种。

3.1 方案一

不升级jdk的版本,修复oracle链接和linux系统参数设置oralce的包活参数CONNECTION_PROPERTY_TCP_KEEPLIVE同时调整服务器的tcp_keeplive_time参数;确保再防火墙关闭链接之前发送一个空报文以防被防火墙杀掉链接。

3.2 方案二

升级jdk到11版本,同时升级oralce的版本到21以上并修改链接参数

CONNECTION_PROPERTY_TCP_KEEPLIVE

和CONNECTION_PROPERTY_TCP_KEEPIDLE。在应用程序里中设置tcp包活和tcp发送报文周期。

  

四、应用

  大数据平台离线计算调用oracle的存储过程中也碰到过类似的情况,由于客户使用的公司其他软件JDK都是1.8的版本,经过和现场同事沟通之后,决定采用第一种方法解决该问题。

posted @ 2022-05-05 20:01  虎啸千峰  阅读(1296)  评论(0编辑  收藏  举报