前言
最近實驗室一直的一個運維項目頻頻在Oracle上出錯,想着錯誤還挺典型,做一個記錄,以供後續參考學習使用。
運維記錄
1. ORA-01000: 超出打開遊標的最大數
問題描述:需要開多進程遍歷對象集合,並且將對象數據存入到Oracle數據庫中,測試報超出Oracle遊標最大數的錯誤。
知識介紹:
Oracle的遊標數cursors:遊標是SQL的一個內存工作區,由系統或用戶以變量的形式定義。遊標的作用就是用於臨時存儲從數據庫中提取的數據塊。在某些情況下,需要把數據從存放在磁盤的表中調到計算機內存中進行處理,最後將處理結果顯示出來或最終寫回數據庫。這樣數據處理的速度纔會提高,否則頻繁的磁盤數據交換會降低效率。(參考https://www.cnblogs.com/guohu/p/11007352.html)
遊標一旦打開,數據就從數據庫中傳送到遊標變量中,然後應用程序再從遊標變量中分解出需要的數據,並進行處理
一般Oracle默認遊標數OPNE_CURSORS爲300(足以夠用),最大爲1000
解決方法:
1. 手動在Oracle中增大遊標數
//查看系統配置遊標數法1
select value from v$parameter where name = 'open_cursors';
//查看系統配置遊標數法2
show parameter open_cursors;
//查看當前被打開的遊標個數
select count(*) from v$open_cursor;
//修改Oracle遊標數
alter system set open_cursors=1000 scope=both;
2. 程序中及時釋放資源,這裏需要明確一般釋放的順序是ResultSet(如果用到的話)、PreparedStatement、Connection。
如果沒有釋放prepareStament就關閉了connection,雖然看起來是釋放了資源,但是遊標數並沒有得到釋放,如果數據量請求量特別大的情況下,每次請求都沒有釋放遊標數,就有可能導致最終Oracle提供的遊標數被佔滿從而報ORA-01000的錯誤。
一般有的可能有二:
1) 關閉Connection(將連接放入連接池)前未關閉PreparedStatement(即遊標)
2) 在循環體中使用connection.prepareStatement(sql)初始化preparedStatement對象後,未在循環體執行完增/刪/改/查後及時關閉到PreparedStatement,導致數據量過大的時候遊標不夠用
for(int i = 0; i < vos.size(); i++){
preparedStatement = connnection.prepareStatement(sql);
preparedStatement.setString(1,vos.getXXX());
preparedStatement.setString(2,vos.getXXX());
preparedStatement.setString(3,vos.getXXX());
preparedStatment.executeUpdate();
//打開遊標後使用完及時關閉
if(preparedStatement != null){
prepatedStatemnet.close();
}
}
此處需要注意的是將程序修改爲及時關閉遊標,但是在多進程併發執行的過程中還是有可能出現ORA-01000的錯誤,這是因爲oracle給的遊標數就是那麼大,如果當前進程數比較大所有的遊標數都被佔了,那麼在有遊標被釋放前會有一段時間資源被用完線程無法執行,等待有遊標可用時即可繼續執行。
總結
以問題爲索引,不斷學習,不斷進步。