防止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参数具体使用事宜,可以参考该连接:
修改截图如下:
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的作业能正常执行了。正常执行的截图示例如下:
![](https://img2022.cnblogs.com/blog/1270011/202205/1270011-20220505195740292-2067450620.png)
将参数tcp_keepalive_time调整为7200s,再次执行作业,发现仍然执行失败。失败截图如下:
![](https://img2022.cnblogs.com/blog/1270011/202205/1270011-20220505195836631-536985680.png)
说明确实是因为防火墙的原因,把长时间处于等待没有进行数据交互的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的版本,经过和现场同事沟通之后,决定采用第一种方法解决该问题。