如何優化大表上的更新?
我正在使用 Oracle 11g,我必須更新 1400 萬行表上的 7 個欄位,更新的列從函式中獲取新值,更新如下所示:
update hslak set HSLAKSTS = reverseheb(hslaksts) , HSLAKSBSTS=reversehebchars(hslaksbsts), HSLAKIVTK=reverseheb(HSLAKIVTK), HSLAKMKOR=reverseheb(HSLAKMKOR), HSLAKIRGTG=reverseheb(HSLAKIRGTG), HSLAKMCR= reverseheb(HSLAKMCR), HSLAKMASL=reverseheb(HSLAKMASL);
它執行了 6-7 個小時,還沒有結束。
創建一個新表而不是更新它會更快嗎?創建一個循環並每次更新 100000 條記錄並在每次迭代後送出會更好嗎?
是程序 I/O 受限(有很多磁碟活動)還是 CPU 受限(如果功能很複雜,這是可能的)?很可能是前者,如果是這樣,那麼如果有索引覆蓋這些列,通常最好在更新每一行並在之後重新創建它們的操作之前刪除它們(儘管不要刪除 PK 和/或聚集索引)。“通常”在這裡是一個重要的詞:在測試環境中檢查以確認您的數據。
創建一個新表而不是更新它會更快嗎?
可能不會,除非新表位於物理上獨立的介質上,因此從舊表讀取並寫入新表不會導致物理磁碟上的磁頭抖動。
創建一個循環並每次更新 100000 條記錄並在每次迭代後送出會更好嗎?
如果您和您的使用者可以處理在任何給定時間部分更新的數據,那麼這(可能行數可能少於 100,000)為您提供了不會阻止您的應用程序用於其他用途的優勢,因為您沒有更新在更新所有行所需的時間長度內鎖定整個表。我沒有過多地使用 postgres,但假設它的行為類似於 MS SQL Server(在簡單恢復模式下),在顯式事務中使用較小的更新可以大大減少日誌文件的增長量,但不太可能顯著影響整體執行時間。
如果批量執行更新並允許在應用程序上繼續正常操作,那麼顯然您不應該遵循第一個建議在開始時刪除索引並在結束時重新創建它們 - 這些索引可能對於合理的性能至關重要應用。
創建一個新表並在那裡插入數據應該更快,因為
update
為重做日誌生成的數據比insert
. 通過使用帶有表的提示insert
可以大大提高批處理速度(您可以找到一些詳細資訊http://www.dba-oracle.com/t_nologging_append.htm)。APPEND``NOLOGGING
分塊送出數據對我來說似乎是一個合理的想法,因為它允許您分步進行遷移,控制重做(和存檔)日誌大小,根據伺服器負載根據需要暫停和恢復更新。
您可以嘗試其他幾件事來使查詢執行得更快。首先,您可以嘗試使用
PARALLEL
提示。當涉及全表掃描並且您有足夠的資源時,它可能會非常有益。請注意,並行執行不是靈丹妙藥,並且可能並非在所有情況下都有效。一些細節可以在這裡找到, http://www.oracle.com/technetwork/articles/database-performance/geist-parallel-execution-1-1872400.html)。另外,我會仔細檢查所涉及功能的效率。是否有可能以最小化呼叫次數的方式重寫它們?我的意思是,例如,
reverseheb
每行呼叫 6 次,其中涉及 6 個上下文切換 SQL/PLSQL 。是否可以重寫函式以便只呼叫一次?此外,從 11g 第 1 版開始,Oracle 允許函式記憶體結果 (
RESULT_CACHE
),這可能會有很大幫助。