2012-03-09 90 views
1

我們的DBA要求我們從一組關聯數組中返回存儲過程中的所有表格數據,而不是使用我在Web上大多數示例中看到的引用遊標。他說,這是因爲Oracle以這種方式做事要快得多,但對我來說這似乎不直觀,因爲數據需要循環兩次,一次在存儲過程中,另一次在應用程序中進行處理。此外,值通常需要從其本地類型轉換爲varchar,以便它們可以存儲在數組中,然後在應用程序端進行回滾。使用這種方法也使得使用orm工具變得困難,因爲他們似乎在大多數情況下都需要引用遊標。Oracle存儲過程,返回引用遊標與關聯數組

的存儲過程的一個例子是下面的:

PROCEDURE sample_procedure (
           p_One  OUT varchar_array_type, 
           p_Two  OUT varchar_array_type, 
           p_Three  OUT varchar_array_type, 
           p_Four  OUT varchar_array_type 
          ) 
IS 
p_title_procedure_name  VARCHAR2(100) := 'sample_procedure'; 
v_start_time DATE :=SYSDATE;  

CURSOR cur 
    IS 
    SELECT e.one, e.two, e.three, e.four FROM package.table 
    WHERE filter='something'; 

    v_counter PLS_INTEGER := 0; 
BEGIN 

    FOR rec IN cur LOOP 
     BEGIN 
      v_counter := v_counter + 1; 
      p_One(v_counter) := rec.one; 
      p_Two(v_counter) := rec.two; 
      p_Three(v_counter) := rec.three; 
      p_Four(v_counter) := rec.four; 
     END; 
    END LOOP; 
END; 

光標用於填充爲每列一個陣列返回。我試圖找到支持他聲稱這種方法更快但卻無法這樣做的信息。任何人都可以填寫我爲什麼他希望我們(.net開發人員)以這種方式編寫存儲過程嗎?

回答

12

DBA的請求沒有意義。

數據庫管理員幾乎可以肯定的想法是,他希望儘量減少從遊標中獲取數據時繼續執行的SQL到PL/SQL引擎上下文轉換的次數。但是,正在提出的解決方案針對這個特殊問題的目標很差,並且在大多數系統中引入了其他更嚴重的性能問題。

在Oracle中,當PL/SQL VM向SQL VM請求更多數據時,會發生SQL到PL/SQL的上下文切換,SQL VM會通過執行語句進一步響應以獲取它隨後打包的數據並提交回到PL/SQL VM。如果PL/SQL引擎一次一個地詢問行,並且您正在獲取很多行,那麼這些上下文變化可能佔整個運行時間的很大一部分。爲了解決這個問題,Oracle至少在8i的時候推出了批量操作的概念。這允許PL/SQL VM從SQL VM一次請求多行。如果PL/SQL虛擬機一次請求100行,您已經消除了99%的上下文轉換,並且您的代碼可能運行得更快。

一旦引入批量操作,就會有很多代碼可以重構,以便通過明確使用BULK COLLECT操作來提高效率,而不是逐行獲取,然後使用FORALL循環來處理那些數據集合。到了10點。但是,2天后,Oracle已將批量操作集成到隱式的FOR循環中,因此隱式的FOR循環現在會自動批量收集100個批次,而不是逐行讀取。

但是,就您而言,由於您要將數據返回給客戶端應用程序,因此批量操作的使用不太重要。任何體面的客戶端API都會具有功能,可以讓客戶端指定在每次網絡往返中需要從光標中提取多少行,並且這些提取請求將直接轉到SQL VM,而不是通過PL/SQL虛擬機,所以沒有SQL來轉換PL/SQL上下文的擔心。您的應用程序必須擔心在每次往返中獲取適當數量的行 - 這足以使應用程序不會在網絡上變得過於健談和瓶頸,但又不會太多,以至於您不得不等待太久才能得到結果返回或將太多數據存儲在內存中。

將PL/SQL集合而不是REF CURSOR返回給客戶端應用程序不會減少發生上下文切換的次數。但它會有其他一些缺點,其中不乏內存使用。 PL/SQL集合必須完全存儲在數據庫服務器上的進程全局區域(PGA)中(假定爲專用服務器連接)。這是必須從服務器的RAM分配的一塊內存。這意味着服務器將不得不分配內存來獲取每個客戶端請求的最後一行。反過來,這又會大大限制應用程序的可伸縮性,並且根據數據庫配置的不同,可能會將RAM從Oracle數據庫的其他部分竊取,這對於提高應用程序性能非常有用。如果你用完PGA空間,你的會話將開始得到與內存有關的錯誤。即使在純粹基於PL/SQL的應用程序中,您也不希望將所有數據提取到集合中,您總是希望以較小的批次提取它,以便儘量減少您使用的PGA數量。

此外,將所有數據提取到內存中會使應用程序感覺速度變慢。幾乎任何框架都會允許您根據需要獲取數據,例如,如果您有報告顯示每個頁面顯示25行,則應用程序只需要在繪製圖像之前獲取前25行第一個屏幕。除非用戶碰巧請求下一頁結果,否則它將永遠不必讀取下一行25行。但是,如果您將數據提取到像DBA提議的那樣的數組中,那麼在您的應用程序可以開始顯示第一行之前,您將不得不提取所有行,即使用戶從不希望看到更多的行行。這意味着數據庫服務器需要更多的I/O來獲取所有行,服務器上有更多的PGA,應用程序服務器上有更多的RAM來緩存結果,以及更長的時間等待網絡。

+0

謝謝。多麼好,詳細的答案!如果可以的話,我會給你兩張票。 – zaq 2012-03-09 20:47:54

0

我相信Oracle會開始從這樣的系統發送結果,因爲它掃描數據庫,而不是全部檢索並將它們發送回來。這意味着結果會在發現時發送,從而加速系統運行。 (實際上,如果我沒有記錯,它會將結果以批量形式返回到循環中。)這主要來自某些訓練的內存

然而真正的問題是爲什麼不直接向他詢問他的推理。他可能指的是甲骨文可以利用的一個技巧,如果你瞭解具體細節,你可以利用速度技巧來充分發揮它的潛力。一般來說,最終的「總是這樣做,因爲這是更快」作爲可疑,值得仔細看看,充分理解他們的意圖。在某些情況下,這實際上並不適用(例如小的查詢結果),其中所有的可讀性問題和開銷都無助於性能。

也就是說,它可以做到保持代碼一致和更快速識別。就他的推理而言,交流是最重要的工具,因爲他知道一個商業祕密,因爲他沒有充分說明問題,所以機會很大。

+0

我問了不止一次,我真正得到的唯一答案是,甲骨文的一些聯繫人告訴他這是事實,我的理解是,這是幾年前。我只是希望有一位Oracle專家能夠明確地告訴我他爲什麼對,或者他爲什麼錯了。 – zaq 2012-03-09 19:43:41

+0

這種方法可以防止結果在掃描時返回。您可能正在考慮用流水線函數來消除返回結果之前處理整個遊標的瓶頸。 – llayland 2012-03-15 04:13:52