记一次jdbc连接oracle数据库占用CPU过高的问题排查

    背景:

    公司有一个通讯系统,主要是通讯数据到客户端程序所指定的数据库,目前支持sqlserver、mysql和oracle三种类型的数据库,此篇主要记录一次oracle数据库占用CPU飙高的问题。

    症状:

    oracle支持上线后,数家客户反馈他们的oracle数据库所在的服务器在程序运行一段时间后CPU飙升,此时查看数据库连接,发现我们的程序占用了大量的数据库连接,有时连接占满后导致在本地用plsql都连不上数据库,会报ORA-00020: maximum number of processes (1000) exceeded错误,严重影响了所有该数据库的使用者

    解决过程:

    由于是之前解决的,某些细节方面可能有疏漏,根据记忆写下大概的解决过程

    一、首先在数据库连接满的情况下,由于登录不上oracle,需要先解决登录oracle的问题,通过查资料发现可以先杀掉一些oracle线程,ps -ef|grep LOCAL   --可以看到很多连接

删除前10条LOCAL=NO的数据库连接:

ps -ef|grep LOCAL=NO|grep -v grep|awk '{print $2}'|head|xargs kill -9

删除过多的连接后,使用plsql登录进数据库

注:关于LOCAL=NO的解释,可参看此文章Oracle 服务器 进程中的 LOCAL=NO 和 LOCAL=YES

    二、查看使用jdbc连接数据库的程序产生的版本数(由于测试环境重现了该问题,所以确定jdbc连接的只有我们的程序)

select sql_id,version_count,SQL_TEXT from v$sqlarea where version_count>= 0 AND module = 'JDBC Thin Client' order by 2 desc ;

    三、找到版本较高的前几个sqlid,查看每次接收的绑定变量的类型,每次的区别在哪里

select position,datatype_string,value_string,LAST_CAPTURED from v$sql_bind_capture where sql_id='amnppbv67qak4' --and rownum<=1000 AND value_string IS NOT NULL;

可以发现相同sql的不同版本差异都在绑定变量的datatype_string值不同造成的,比如同样的字段,同样的绑定变量,第一次update时,传进来的值为null,则此时oracle会自动使用VARCHAR2(32)来接收,若下次传进来的值大于VARCHAR2(32)长度,则oracle会使用VARCHAR2(128)来接收(参考BIND_MISMATCH导致过多VERSION COUNT的问题),则针对这个sql则会重新硬解析,生成一个新的版本和执行计划,此处会占用大量的服务器资源。

    四、此时为了数据库可以尽快的恢复运转,可执行如下sql来清除shared_pool,执行后数据库可暂时恢复正常(好像执行该语句需先停止其他程序对该数据库的访问,否则会一直卡在那里):

alter system flush shared_pool;

综上,出现该问题的原因主要有两种情况:

    1.待insert或update的表中有较多可空字段,jdbc执行语句时,传进去绑定变量时使用了statement.setObject()方法,此时即使是非字符型的字段,oracle也会使用VARCHAR2(32)来接收,而之后传进具体的值时,oracle就会根据字段类型使用新的类型来接收该绑定变量的值,同时硬解析生成新版本和执行计划;

    2.通讯某些字符型字段特别多的表,且字段长度不固定的情况下,较大可能出现这种问题。

    解决方法:

    针对原因1,目前在程序中做了修改,除了clob等特殊类型外,日期或数值等类型字段,都统一使用statement.setString()方法去赋值,oracle可以自动转化为相对应的类型入库;

    针对原因2,没有特别好的方法,参考BIND_MISMATCH导致过多VERSION COUNT的问题,设置了oracle接收绑定变量时,直接使用VARCHAR2(2000)来接收

alter system set events '10503  trace name context forever ,level 2000'

此解决方法不是特别妥当,不过确实可以解决该问题,至于时候会引起其他问题,暂时还未发现。

    该问题其实是由oracle的版本BUG引起的(截自由 bind_mismatch 引起的 大量 version_count 问题)


故升级oracle版本自然是最稳妥的解决方案,不过由于有些客户可能不愿意升级,所以此处提供了自己的解决方案,具体效果还需后续观察





猜你喜欢

转载自blog.csdn.net/z3261743/article/details/79850666