回答
在Oracle中,有一個SQL虛擬機(VM)和一個PL/SQL VM。當您需要從一個虛擬機遷移到另一個虛擬機時,會產生上下文切換的代價。單獨來說,這些上下文轉換相對較快,但是當您逐行處理時,它們可以佔用代碼花費的很大一部分時間。當您使用批量綁定時,您可以將多行數據從一個虛擬機移動到另一個虛擬機,只需一次上下文切換,顯着減少上下文切換次數,使代碼更快。
以一個明確的遊標爲例。如果我寫這樣的事情
DECLARE
CURSOR c
IS SELECT *
FROM source_table;
l_rec source_table%rowtype;
BEGIN
OPEN c;
LOOP
FETCH c INTO l_rec;
EXIT WHEN c%notfound;
INSERT INTO dest_table(col1, col2, ... , colN)
VALUES(l_rec.col1, l_rec.col2, ... , l_rec.colN);
END LOOP;
END;
然後我每次執行取,我
- 執行從PL/SQL VM到SQL VM
- 上下文轉移問計於SQL VM執行光標以生成下一行數據
- 執行從SQL VM到PL/SQL VM的另一個上下文轉換以返回我的單行數據
每當我插入一行時,我都會做同樣的事情。我承擔了將PL/SQL VM中的一行數據發送到SQL VM的上下文轉換成本,要求SQL執行INSERT
語句,然後將其他上下文轉換回PL/SQL。
如果source_table
有100萬行,那就是400萬的上下文轉換,這可能會佔我的代碼耗費時間的合理部分。另一方面,如果我執行的BULK COLLECT
的LIMIT
爲100,那麼我可以通過從SQL VM檢索100行數據到PL/SQL中的集合中來減少99%的上下文轉換次數,每當我在那裏發生上下文轉換時,上下文轉換並將100行插入到目標表中。
如果可以重寫我的代碼使用批量操作
DECLARE
CURSOR c
IS SELECT *
FROM source_table;
TYPE nt_type IS TABLE OF source_table%rowtype;
l_arr nt_type;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_arr LIMIT 100;
EXIT WHEN l_arr.count = 0;
FORALL i IN 1 .. l_arr.count
INSERT INTO dest_table(col1, col2, ... , colN)
VALUES(l_arr(i).col1, l_arr(i).col2, ... , l_arr(i).colN);
END LOOP;
END;
的現在,我每次執行提取時間,我取回100行數據到我的收藏與一組情境的變化的。每當我做我的FORALL
插入,我插入100行與一組上下文轉移。如果source_table
有100萬行,這意味着我已經從400萬上下文轉換到40,000上下文轉換。如果上下文的變化佔據了我代碼已用時間的20%,我已經消除了19.8%的流逝時間。
您可以增加LIMIT
的大小以進一步減少上下文轉換次數,但您很快就會遇到收益遞減規律。如果您使用1000而不是100的LIMIT
,那麼您將消除99.9%的上下文偏移,而不是99%。這意味着您的收藏使用了10倍以上的PGA內存。在我們的假設例子中,它只會消除0.18%的時間。通過消除額外的上下文偏移,您很快就會達到您正在使用的額外內存所增加的時間超過您節省的時間。一般而言,100到1000之間的某個LIMIT
很可能是最佳選擇。
當然,在這個例子中,這將是更有效的還是要消除所有方面的變化,並在單個SQL語句
INSERT INTO dest_table(col1, col2, ... , colN)
SELECT col1, col2, ... , colN
FROM source_table;
它只會意義求助於在PL/SQL做的一切如果你正在對源表中的數據進行某種操作,而這些操作無法在SQL中合理實現,那麼首先要考慮這一點。
此外,我在示例中故意使用了顯式遊標。如果您使用隱式遊標,則在最新版本的Oracle中,隱式地獲得BULK COLLECT
和LIMIT
的好處。還有另一個StackOverflow問題,討論相關的performance benefits of implicit and explicit cursors with bulk operations,這些問題涉及更多有關這些特定皺紋的細節。
據我所知,有兩個引擎,PL/SQL engine and SQL Engine。執行查詢,使在一個時間使用一個發動機的比兩個
實施例之間的切換更有效:
INSERT INTO t VALUES(1)
由SQL發動機而
FOR Lcntr IN 1..20
END LOOP
由PL執行處理的/ SQL引擎
如果將上述兩個語句組合在一起,將INSERT放入循環中,
FOR Lcntr IN 1..20
INSERT INTO t VALUES(1)
END LOOP
對於每次(20)次迭代,Oracle將在兩個引擎之間切換。 在這種情況下,建議使用PL/SQL引擎執行BULK INSERT。
- 1. 引導性能收集
- 2. MongoDB上限收集性能
- 3. TryTake阻止收集性能
- 4. FLV性能和垃圾收集
- 5. C#和.Net垃圾收集器性能
- 6. 性能比較收集方法
- 7. Linux中的性能數據收集(API)
- 8. 最佳搜索性能收集策略?
- 9. 用mybatis收集resultMap的性能問題
- 10. 去大內存垃圾收集性能
- 11. 設置Windows 7 Perfmon以收集性能數據 - 要收集哪些數據?
- 12. 是否有可能過濾OData內收集和收集的屬性
- 13. 收集性能指標的集合框架
- 14. 通緝:審查收集庫的功能/問題/性能
- 15. 收集配置屬性「TimeToLive」
- 16. 收集屬性的排序
- 17. 羣集性能
- 18. 「不能收集變量」?
- 19. 用Corona SDK不能'收集'
- 20. 不能在MongoDB中收集
- 21. LISTAGG功能與PLSQL收集
- 22. 的MongoDB只能從收集
- 23. 我怎樣才能收集
- 24. 骨幹不能從收集
- 25. 垃圾收集期間是否會檢查持久性收集?
- 26. Java 7未能收集由java收集的永久代5
- 27. 如何收集性能/分析數據作爲JVM啓動
- 28. 用於性能評估的java統計信息收集
- 29. 垃圾收集器在不同的機器上的性能
- 30. 如何在Flask應用程序中收集性能指標?
你最後一句話是有點欺騙。 BULK使上下文切換僅發生一次,儘管它仍然發生。 – viper 2015-03-02 15:44:16