1. 程式人生 > >記一次jdbc連線oracle資料庫佔用CPU過高的問題排查

記一次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版本自然是最穩妥的解決方案,不過由於有些客戶可能不願意升級,所以此處提供了自己的解決方案,具體效果還需後續觀察