2010-05-27 51 views
4

我必須改進一些從Java程序調用Oracle存儲過程的代碼。目前代碼真的非常慢:在我的開發機器上高達大約8秒鐘。在同一臺機器上,如果我直接調用一個執行相同處理並返回相同數據的SQL查詢,則需要100   ms ...爲什麼從Oracle存儲過程中檢索ResultSet非常慢?

該代碼創建一個CallableStatement,將輸出參數之一註冊爲用Oracle光標,然後使用檢索語句的getObject方法光標和解析它的ResultSet:

cstmt = conn.prepareCall("{ call PKG_ESPECEW.P_ListEspece(?, ?, ?, ?, ?, ?) }"); 
cstmt.registerOutParameter(4, oracle.jdbc.OracleTypes.CURSOR); 
[...] 
cstmt.executeQuery(); 
rs = (ResultSet)cstmt.getObject(4); 
rs.setFetchSize(1000); //supposed to help ? 

options = new HashMap<String, String>(1000); 
rs.next() //added that to measure exactly the length of the first call 

while(rs.next()) { 
    [...] 
} 

我把一些時間戳的代碼知道哪個部分佔用了這麼久。結果:第一次撥打rs.next()需要幾秒鐘的時間。結果集是平均值,從10到幾千行。正如我之前所說的,處理來自普通PreparedStatement的類似結果集取決於大小需要10-100   ms。

代碼有什麼問題嗎?我如何改進它?如果我沒有任何其他解決方案,我會直接在SQL中執行SQL,但是我更喜歡使用不允許重寫所有過程的解決方案!

這裏是存儲過程的定義:

PROCEDURE P_ListEspece(P_CLT_ID IN ESPECE.ESP_CLT_ID%TYPE,  -- Langue de l'utilisateur 
         P_ESP_GROUP_CODE IN ESPECE.ESP_CODE%TYPE,-- Code du groupe ou NULL 
         P_Filter IN VARCHAR2,     -- Filtre de la requête 
         P_Cursor OUT L_CURSOR_TYPE,    -- Curseur 
         P_RecordCount OUT NUMBER,    -- Nombre d'enregistrement retourne 
         P_ReturnStatus OUT NUMBER);    -- Code d'erreur 
+0

有沒有你不使用'OracleCallableStatement的理由。getCursor'而不是'getObject'? – 2010-05-27 15:31:25

+1

我必須將CallableStatement強制轉換爲OracleCallableStatement,但我使用的是DBCP,而CallableStatement實際上是DBCP提供的「代理」,所以我會得到一個異常(我試過了)。 – 2010-05-27 15:47:50

+1

對於抓取,要通過網絡進行優化,您應該使用setRowPrefetch。這產生了巨大的影響。雖然setFetchSize約爲20-30%,但在大型數據集上預取可高達10倍。 – zhrist 2016-08-11 11:44:12

回答

3

「我認爲程序執行,那麼它存儲在Oracle服務器的內存結果,最後通過光標回傳給客戶端(Java應用程序)和結果集和JDBC」

這是不正確。 Oracle作爲遊標返回的內容基本上是一個指向查詢的指針(所有這些指針都可以使用任何綁定變量)。它沒有實現內存中的結果集。它可能是一個數以百萬計/十億行的巨大結果集。

所以這可能是一個緩慢的查詢,需要很長時間才能提供結果。

+0

我與處理數據庫的DB人討論過,似乎在SP內部執行的查詢實際上是問題,因爲它調用的是爲找到的每個記錄打開另一個遊標的函數。他刪除了這個電話,執行時間縮短到不到1秒。它仍然非常緩慢,但已經更可以接受。我想查詢本身仍然可以改進,問題實際上是查詢而不是數據的傳輸。 – 2010-05-28 07:18:24

1

顯然該存儲過程是做一些數據變換/按摩來回(例如int < - >varchar)。這是衆所周知的需要大量的時間在大桌子的情況下。確保您已經在SP參數中聲明瞭正確的數據類型,並在CallableStatement中設置了正確的數據類型。

+0

實際上它不是讀取或設置需要時間的SP參數,而是從ResultSet中讀取,它本身就是SP的一個參數。我會用SP定義更新這個問題。所以你認爲在這種情況下類型轉換可能是一個問題? – 2010-05-27 15:50:17

+0

你說'PreparedStatement'更快,所以問題可能發生在SP上。如果需要(隱式)轉換任何列值,則需要花費時間,尤其是在SP的WHERE子句中使用時。 – BalusC 2010-05-27 15:54:13

+0

但它不是cstmt.executeQuery();這很慢,這是結果集瀏覽。我認爲這個過程被執行了,然後它的結果存儲在oracle服務器的內存中,最後通過遊標,結果集和JDBC傳輸回客戶端(java應用程序)。但也許我錯了?我是Oracle/PLSQL的無知者。讀取結果集時,程序是否會「一步一步」執行? – 2010-05-27 16:01:30

1

在Java之外執行程序需要多長時間?請在SQL *這樣的腳本加:

var ref refcursor 
var cnt number 
var status number 
exec p_listespece (xx, yy, zz, :ref, :cnt, :status);--replace with actual values 
print :ref 

如果需要超過10-100毫秒,您的問題可能來自於存儲過程。

+0

其實我沒有SQLPlus或安裝在我的開發機器上的oracle客戶端。我使用SQLDevelopper完成大部分thnigs,然後向DBA請教。我明天會和他聯繫。或者你知道我是否可以直接從SQLDev執行plsql? – 2010-05-27 16:40:15

+0

我安裝了oracle客戶端和sql plus,並試過這個。結果在命令行中的顯示是永久性的,但是它可能被windows命令行中的命令行打印放慢了。無論如何,問題在於SP本身。 – 2010-05-28 07:20:17

0

我有同樣的問題,我們通過將返回的參數從遊標更改爲varchar來解決(我和oracle專用人員),這是存儲在內部執行的普通查詢。 這是一個巨大的實施,我不知道這是否適用於您的情況。

這裏的片段:

`

String sql = "call MyStored(?,?,?,?)"; 
CallableStatement st = Conn.prepareCall(sql); 
st.setInt(1, 10); 
st.setInt(2, 20); 
st.setInt(3, 30); 
st.registerOutParameter(4, OracleTypes.VARCHAR); 

st.execute(); 

String query = (String) st.getObject(4); 
Statement stmt = Conn.createStatement(); 
rs = stmt.executeQuery(query); 
[...] 
//work with resultset 
[...] 
stmt.close(); 
stmt = null; 

`