我正在處理一個數據倉庫項目,因此,我一直在實現一些ETL函數的包。我在開發筆記本電腦時遇到了一個問題,並認爲它與我的oracle安裝有關,但現在它已經「傳播」到生產服務器。 兩個功能「有時」變得難以置信的緩慢。我們已經實現了一個日誌記錄系統,使我們在每個x行的日誌記錄表上輸出結果。當功能通常需要每塊大約10秒時,「有時」功能需要長達3分鐘。在重建一些索引並重新啓動該功能之後,它和以前一樣快。 不幸的是,我無法確切知道它是哪個索引,因爲重新啓動函數並構建它用於其工作的光標需要一些時間,我們沒有時間自行檢查每個索引,所以我只是重建所有可能被該函數使用的索引並重新啓動它。Oracle索引「打破」
出現問題的函數使用遊標從表中選擇約5000萬到200萬個條目中的數據,並通過一個包含大約50-500個條目的小表進行連接。連接條件是字符串比較。然後,我們使用從聯接獲得的小表中的主鍵更新主表上的外鍵。更新過程由循環完成,這已經證明可以節省大量時間。
下面是兩個表的表結構的簡化版本:
CREATE TABLE "maintable"
( "pkmid" NUMBER(11,0) NOT NULL ENABLE,
"fkid" NUMBER(11,0),
"fkstring" NVARCHAR2(4) NOT NULL ENABLE,
CONSTRAINT "PK_MAINTABLE" PRIMARY KEY ("pkmid");
CREATE TABLE "smalltable"
( "pksid" NUMBER(11,0) NOT NULL ENABLE,
"pkstring" NVARCHAR2(4) NOT NULL ENABLE,
CONSTRAINT "PK_SMALLTABLE" PRIMARY KEY ("pksid");
兩個表都有自己的字符串列索引。添加主鍵時,我會在每次發生問題時重建4個索引。
我們以某種方式得到我們的數據,我們只有可用維護中的fkstring和fkid設置爲null。在第一步中,我們填充小表。這隻需要幾分鐘,並按以下方式完成:
INSERT INTO smalltable (pksid, pkstring)
SELECT SEQ_SMALLTABLE.NEXTVAL, fkstring
FROM
(
SELECT DISTINCT mt.fkstring
FROM maintable mt
MINUS
SELECT st.pkstring
FROM smalltable st
);
commit;
此功能從不會引起任何問題。
下面的函數(它是功能的簡化版本 - 我已刪除的日誌記錄和異常處理,並改名一些變量):
function f_set_fkid return varchar2 is
cursor lCursor_MAINTABLE is
SELECT MT.PKmID, st.pksid
FROM maintable mt
JOIN smalltable st ON (mt.fkstring = st.pkstring)
WHERE mt.fkid IS NULL;
lIndex number := 0;
lExitLoop boolean := false;
type lCursorType is table of lCursor_MAINTABLE%rowtype index by pls_integer;
lCurrentRow lCursor_MAINTABLE%rowtype;
lTempDataArray lCursorType;
lCommitEvery constant number := 1000;
begin
open lCursor_MAINTABLE;
loop
-- get next row, set exit condition
fetch lCursor_MAINTABLE into lCurrentRow;
if (lCursor_MAINTABLE%notfound) then
lExitLoop := true;
end if;
-- in case of cache being full, flush cache
if ((lTempDataArray.count > 0) AND (lIndex >= lCommitEvery OR lExitLoop)) then
forall lIndex2 in lTempDataArray.FIRST..lTempDataArray.LAST
UPDATE maintable mt
set fkid = lTempDataArray(lIndex2).pksid
WHERE mt.pkmid = lTempDataArray(lIndex2).pkmid;
commit;
lTempDataArray.delete;
lIndex := 0;
end if;
-- data handling, fill cache
if (lExitLoop = false) then
lIndex := lIndex + 1;
lTempDataArray(lIndex). := lCurrentRow;
end if;
exit when lExitLoop;
end loop;
close lCursor_MAINTABLE;
return null;
end;
我將是任何幫助非常感謝。
P.S.我知道批量收集可以加快函數的速度,也可能稍微緩解代碼,但目前我們滿足於通常所具有的函數的速度。改變使用批量收集的功能在我們明年的計劃中,但目前它不是一個選項(我懷疑它會解決這個索引問題)。
我一直在看這段代碼一段時間,但它對我來說沒有任何意義。出現很多問題。我有強烈的印象,這個設置需要重新設計。可能把所有的邏輯放在一個更新語句中。爲什麼在返回null時使用函數?首先清理邏輯,然後開始優化性能。 – 2011-12-20 11:43:57
簡單更新語句的數據太多。嘗試在單個語句中更新大型表上的200萬行,然後運行到表空間溢出。至於函數而不返回任何東西,請忽略它。我們使用這個從函數獲得的一些反饋返回到調用函數,但是我已經從這個示例中刪除了它。現在這個函數在我們的數據上運行了好幾個星期,除了索引被破壞之外。 – 2011-12-20 12:32:44
更新200 miljoen行不是太多。但我強烈建議你清理代碼的程序流程。我無法獲得使用lTempDataArray背後的邏輯 – 2011-12-20 14:09:08