- 1. 序
在歷史數據轉出測試過程中,通過不斷的優化,包括SQL調整和數據庫調整,從AWR中看到,基本上難以進行更多的性能提升,於是准備試試並行執行的特性,從這個任務的特點來分析,也比較適合采用這項技術。本文介紹了使用並行執行過程中的常用SQL,以及遇到的一些問題,以及性能對比試驗的結果,並且分享了一些問題的解決經驗,分析了適合並行執行的場景。
- 2. 概念及常用語法
並行執行通過充分利用硬件資源來實現特定任務的性能提升,將一個SQL語句同時分布到多個CPU上去執行,從而縮短總的耗時。
Oracle的並行執行包括:
1) 並行查詢
2) 並行DML(insert,delete,update)
3) 並行DDL(表和索引的創建)。
為了方便參考使用,下面將收集的常用語句按並行執行的三個級別進行介紹:
l 對象級
設置表和索引的並行度,從而使用涉及這些對象的SQL操作按設定的並行度執行。
例如:alter table 門診費用記錄 parallel 8;
alter index 門診費用記錄_IX_登記時間 parallel;
如果不指定並行度的值,Oracle會根據參數和CPU數來估算一個缺省值。
禁用並行度(指定並行度為1或使用noparallel):
alter table 門診費用記錄 parallel 1;
alter index 門診費用記錄_IX_登記時間 noparallel;
如果不改變對象的並行度屬性,唯一可以禁用並行查詢的方法是將初始化參數parallel_max_servers設置為0。
l 會話級
人工啟用和禁用的語法如下:
ALTER Session ENABLE PARALLEL query[|DML|DDL];
ALTER Session DISABLE PARALLEL query[|DML|DDL];
強制按指定的並行度執行。
ALTER Session FORCE PARALLEL QUERY PARALLEL 8;
ALTER Session FORCE PARALLEL DML PARALLEL 8;
ALTER Session FORCE PARALLEL DDL PARALLEL 8;
強制並行對於遞歸SQL不起作用,但覆蓋表或索引上定義的並行度。
查詢當前會話是否啟用了並行執行:
(Oracle 10.2.0.1上,缺省啟用了並行QUERY和DDL,沒有啟用DML)
SQL> SELECT pq_status ,pdml_status, pddl_status
FROM v$session WHERE sid=sys_context('userenv','sid');
PQ_STATUS PDML_STATUS PDDL_STATUS
--------- ----------- -----------
ENABLED DISABLED ENABLED
l 語句級
通過在SQL中添加提示來指定並行執行及並行度。
優化器只是按指定的提示來考慮是否使用並行執行,並不會強制使用(它會選擇成本最低的那一個執行計划)
例如:
Select /*+ parallel(t1,8)*/count(*) from 門診費用記錄 t1;
Create table 醫囑執行時間 parallel 8 as
select /*+ parallel(t1,8)*/* from醫囑執行時間 t1;
alter index 門診費用記錄_IX_登記時間 rebuild parallel 8;
注意:
並行DML需要先在會話級顯示的啟用,並且需要同時啟用並行查詢;
例:
SQL>ALTER Session ENABLE PARALLEL DML;
SQL> ALTER Session ENABLE PARALLEL QUERY;
SQL> Update /*+ parallel(t1,8)*/ 門診費用記錄 t1
Set 待轉出 = 132
Where 結帳id In
(Select /*+ parallel(t2,8)*/結帳id From 病人預交記錄 t2 Where 待轉出 = 132);
如果不提交事務,該會話的后續SQL無法訪問被修改的表,查詢未提交事務的表,將會返回錯誤:”ora-12838:無法讀取、修改一個被並行修改過的表”
定義主鍵約束時,無法並行的自動創建主鍵索引,但可以采取以下變通方式:
CREATE UNIQUE INDEX 檢驗標本記錄_UQ_標本序號
ON 檢驗標本記錄(核收時間, 儀器ID, 標本序號, 標本類別) PARALLEL 8;
ALTER TABLE 檢驗標本記錄 ADD CONSTRAINT
檢驗標本記錄_UQ_標本序號 Unique (核收時間, 儀器ID, 標本序號, 標本類別);
這種方式創建的主鍵約束與自動創建的有一個差別,就是刪除主鍵的時候,不會自動刪除對應的索引,需要增加刪除索引的語法,例:
ALTER TABLE 檢驗標本記錄 drop
CONSTRAINT 檢驗標本記錄_UQ_標本序號 cascade drop index;
另外,關於並行查詢的參數配置,大部分情況下,無須調整,網上的資料比較多,這里不再一一列舉。僅說明一個參數:
當執行並行重建索引時,可能會遇到ora-00600錯誤,通過修改參數parallel_execution_message_size可解決這個問題,例:
SQL> alter system set parallel_execution_message_size=8192 scope=spfile;
缺省值為2148,對於一般的並行任務,這個值太小。
修改后需重啟數據庫。
- 3. 使用效果
l 並行查詢及並行DML
由於所使用的歷史數據轉出,大部分查詢均是索引范圍掃描,沒有全表掃描,所以,不適合並行執行。
但是,由於一次意外操作:索引壓縮重建(並行DDL),執行后導致索引的屬性自動加上了並行度,導致相關的SQL查詢自動啟用了並行查詢,結果,執行計划采用了大表全表掃描,采用hash連接或嵌套連接,導致查詢異常緩慢。
一些復雜的SQL執行超過了一個小時,甚至下面這種簡單的SQL執行超過了5個小時仍然沒有返回結果:
Update /*+ rule*/ 病人醫囑計價
Set 待轉出 = n_批次
Where 醫囑id In (Select ID From 病人醫囑記錄 Where 待轉出 = n_批次);
查看執行計划,發現采用了全表掃描+嵌套連接索引的方式。
並且,提示字rule失效,優化器模式變成了CBO。最后,取消了索引的並行度,然后,重建索引,禁用了壓縮特性,最終執行計划才恢復了正常。
因為普通的索引范圍掃描並不能使用並行查詢(除非是分區索引),所以,優化器會選擇全表掃描方式,但是很多時候這並不是我們所期望的訪問方式。
所以,並行查詢和並行DML一定要慎重,否則,性能差別非常大。
l 直接路徑插入
為了快速的加載大量數據,采用直接路徑插入方式可以大幅提升插入性能。
直接路徑插入自動對insert 和Select操作采用了並行執行,並且目標表采用nologging最小日志模式的話,試驗表明,最高可減少5倍的耗時。
例:
Insert Into /*+ append*/H住院費用記錄(ID, 記錄性質, NO, ……)
Select ID, 記錄性質, NO, …… From 住院費用記錄 Where 待轉出 = 132;
l 並行DDL
歷史數據轉出過程中,需要重建轉出表上的查詢所用到的索引,以便及時回收空間,加快查詢速度,但這個索引重建的過程非常耗時。測試環境的配置為:32G內存、32路CPU(4*8)、Raid10的SCSI硬盤。
測試結果表明:
- 並行DDL要快50%以上。
沒有並行時,重建索引需要36分鍾,並行執行只需要16分鍾。
- 並不是並行度越高就越快。
因為磁盤IO所限,該環境下並行度為8時最快
l 並行收集統計信息
收集對象統計信息的時候,有一個參數可以指定並行度,並行的效果非常明顯。試驗表明,收集ZLHIS的所有對象,並行執行可以將整個時間由1個半小時縮短到30分鍾左右。
- 4. 小結
並行執行屬於Oracle的OLAP應用特性之一,如果你有一些耗時很長的任務,並且服務器有大量的閑置資源(CPU,內存,IO帶寬),則比較適合采用並行執行技術,它可以給你帶來成倍的性能提升效果,否則,並行執行可能會由於大量的消耗資源從而影響其他人的正常使用。所以,並行執行一般情況,較少的在OLTP場合中應用,因為通常我們並不希望某一個人占用過多的系統資源。
- 5. 參考資料
1) Oracle 性能診斷藝術.Christian Antognini.2009
2) Oracle 性能優化求生指南.Guy Harrison .2012
- 6. 附:索引重建並行對比測試
--無並行
22:26:19 SQL> exec Zl1_Datamove_Reb(100, 1, 6);
PL/SQL procedure successfully completed
Executed in 2199.734 seconds
--------------------------------------------------------------
SQL> ALTER session FORCE PARALLEL DDL PARALLEL 24;
SQL> exec Zl1_Datamove_Reb(100, 1, 6);
PL/SQL procedure successfully completed
Executed in 1450.828 seconds
--------------------------------------------------------------
SQL> ALTER session FORCE PARALLEL DDL PARALLEL 16;
SQL> exec Zl1_Datamove_Reb(100, 1, 6);
PL/SQL procedure successfully completed
Executed in 1272.063 seconds
--------------------------------------------------------------
SQL> ALTER session FORCE PARALLEL DDL PARALLEL 8;
SQL> exec Zl1_Datamove_Reb(100, 1, 6);
PL/SQL procedure successfully completed
Executed in 1018.765 seconds
--------------------------------------------------------------
SQL> ALTER session FORCE PARALLEL DDL PARALLEL 4;
SQL> exec Zl1_Datamove_Reb(100, 1, 6);
PL/SQL procedure successfully completed
Executed in 1125.719 seconds