PLSQL中慎用CLOB型別
在老熊《在儲存過程中的clob資料型別》()中,
分析了PLSQL中使用clob欄位,雖然clob型別便於使用(不用擔心欄位長度超過varchar2的限制),但也帶來了效能問題。
透過對比分析發現PLSQL中對clob欄位的操作,引發以下問題並給出了相應建議:
*大量邏輯讀(一致性讀(consistent gets)和當前模式讀(db block gets))
*臨時段,只有在會話退出時才會清除
為方便參考,複製原文如下:
---------------------------------------
在儲存過程中的clob資料型別Oracle效能最佳化performance9月 28th, 2011
客戶新上線的一套重要生產系統,某個儲存過程每小時呼叫約11萬次,每次呼叫的邏輯讀超過了10000,消耗的CPU佔資料庫CPU Time的25-30%。很顯然,這樣一個儲存過程是值得最佳化的。
不幸的是,這個儲存過程的業務邏輯很複雜,光是引數就有9個。而儲存過程所在的包其程式碼超過了1萬行。透過複查程式碼的方式,耗時耗力,還不一定能夠找出問題。
診斷效能問題,首要的是瞭解這個儲存過程所大概要完成的業務功能,然後透過trace或instrument收集足夠詳細的效能資料。從客戶和開發商那裡瞭解到,存在效能問題的儲存過程,主要用於系統之間的資料查詢介面,根據不同的引數輸入查詢不同的資料,那麼,對於這種小資料量的儲存過程,可以考慮使用10046事件來分析是哪些SQL產生了這麼多的邏輯讀。
但是透過10046事件,發現儲存過程中實際執行的SQL並不多,同時並沒有邏輯讀高的SQL語句。也許問題並沒有出現在SQL語句中,而是出現在儲存過程中其他非SQL部分。雖然用10046沒有找到SQL語句,但還是有重大發現,在過程執行時,大量的邏輯讀來自於current方式的讀,這顯示不是通常的SELECT語句所產生的。接下來我們用dbms_profiler來分析儲存過程:
select dbms_profiler.start_profiler from dual;
exec intf.CRM_SERVICE_INTF.QueryService(.....);
exec dbms_profiler.stop_profiler;
然後使用來自MOS文件“Implementing and Using the PL/SQL Profiler [ID 243755.1]”中的profiler.sql指令碼,生成一個profiler的結果檔案,格式為html。下面是部分的內容:
點選其顯示的程式碼行(line),跳轉到相應的原始碼,發現大量的字串拼接程式碼,很明顯是用於拼接成XML格式。這很容易理解,因為現在系統之間的文字資料互動,xml幾乎成了標準。從上面的截圖中也可以看到很多型別於xml:=xml || ‘xxxx’ 這樣的程式碼。是這樣的程式碼引起的問題嗎?
檢查程式碼發現,這裡用於拼接字串的變數xml,被定義為clob型別,這引起了我極大的關注。在oracle的標準資料型別中,lob型別由於其能夠儲存大資料的本質,導致其內部格式和操作是最複雜的。有理由懷疑是clob的大量拼接引起的問題。
在有懷疑物件後,我們可以構造下面的測試來進行驗證:
首先建立下面3個不同的儲存過程,但是實現的功能是一致的:
create or replace procedure p1 ( v_out out clob)
is
v_lob clob;
begin
v_lob:='';
for rec in (select object_name from dba_objects where rownum< =1000) loop
v_lob:=v_lob || rec.object_name;
end loop;
v_out:=v_lob;
end;
/
create or replace procedure p2 ( v_out out clob)
is
v_lob varchar2(32767);
begin
v_lob:='';
for rec in (select object_name from dba_objects where rownum<=1000) loop
v_lob:=v_lob || rec.object_name;
end loop;
v_out:=v_lob;
end;
/
create or replace procedure p3 ( v_out out clob)
is
v_lob clob;
v_str varchar2(32767);
v_cnt number;
begin
v_lob:='';
v_cnt:=0;
v_str:='';
for rec in (select object_name from dba_objects where rownum<=1000) loop
v_str:=v_str || rec.object_name;
v_cnt:=v_cnt+1;
if v_cnt = 50 then
v_cnt:=0;
v_lob:=v_lob || v_str;
v_str:='';
end if;
end loop;
if v_cnt <>0 then
v_lob:=v_lob || v_str;
end if;
v_out:=v_lob;
end;
/
第1個儲存過程,P1,完全使用clob型別來拼接字串;第2個儲存過程,P2,使用varchar2型別拼接字串,只有在過程最後將varchar2轉成clob;第3個儲存過程,P3,先使用varchar2型別拼接成較長的字串,然後再用較長的字串用clob型別來拼接。
下面看看P1和P2的差距:
SQL> var v_lob1 clob;
SQL> var v_lob2 clob;
SQL> begin
2 sys.runstats_pkg.rs_start;
3 p1(:v_lob1);
4 sys.runstats_pkg.rs_middle;
5 p2(:v_lob2);
6 sys.runstats_pkg.rs_stop;
7 end;
8 /
Run1 ran in 5 hsecs
Run2 ran in 0 hsecs
run 1 ran in 50000000% of the time
Name Run1 Run2 Diff
STAT...lob reads 999 0 -999
LATCH.cache buffers lru chain 1,000 0 -1,000
STAT...free buffer requested 1,012 9 -1,003
STAT...lob writes 2,000 1 -1,999
STAT...lob writes unaligned 2,000 1 -1,999
LATCH.object queue header oper 2,024 9 -2,015
STAT...consistent gets 3,581 580 -3,001
STAT...consistent gets from ca 3,581 580 -3,001
STAT...consistent changes 5,051 42 -5,009
STAT...db block changes 5,061 52 -5,009
STAT...calls to get snapshot s 7,008 10 -6,998
STAT...db block gets from cach 14,075 57 -14,018
STAT...db block gets 14,075 57 -14,018
STAT...session logical reads 17,656 637 -17,019
LATCH.cache buffers chains 46,151 1,083 -45,068
STAT...session uga memory 138,608 15,072 -123,536
STAT...session uga memory max 315,052 131,024 -184,028
STAT...session pga memory max 393,216 131,072 -262,144
STAT...session pga memory 393,216 65,536 -327,680
Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
49,789 1,674 -48,115 2,974.25%
PL/SQL 過程已成功完成。
從上面的資料來看,差距是巨大的,執行P1時產生的一致性讀(consistent gets)和當前模式讀(db block gets),都遠遠大於執行P2時產生的一致讀和當前模式讀。特別是大量的db block gets驗證了之前使用10046事件跟蹤儲存過程執行得到的結果。上面的資料中,甚至是會話消耗的記憶體都有很明顯的差異。同時,還有其他對效能顯著影響的地方,cache buffers chains latch,cache buffers lru chain latch,object queue header operation latch,這3種latch的獲取次數是相當多的,在大併發時無疑將引起爭用。實際上,在客戶的這套系統中,這3種latch的爭用經常出現,只是目前還沒形成特別嚴重的後果。
再看看P1和P3的差異:
SQL> var v_lob1 clob;
SQL> var v_lob2 clob;
SQL> begin
2 sys.runstats_pkg.rs_start;
3 p1(:v_lob1);
4 sys.runstats_pkg.rs_middle;
5 p3(:v_lob2);
6 sys.runstats_pkg.rs_stop;
7 end;
8 /
Run1 ran in 7 hsecs
Run2 ran in 2 hsecs
run 1 ran in 350% of the time
Name Run1 Run2 Diff
STAT...lob reads 999 19 -980
LATCH.cache buffers lru chain 1,002 20 -982
STAT...free buffer requested 1,012 29 -983
STAT...lob writes 2,000 40 -1,960
STAT...lob writes unaligned 2,000 40 -1,960
LATCH.object queue header oper 2,016 49 -1,967
STAT...consistent gets from ca 3,579 639 -2,940
STAT...consistent gets 3,579 639 -2,940
STAT...consistent changes 5,051 143 -4,908
STAT...db block changes 5,061 153 -4,908
STAT...calls to get snapshot s 7,007 146 -6,861
STAT...db block gets 14,075 346 -13,729
STAT...db block gets from cach 14,075 346 -13,729
STAT...session logical reads 17,654 985 -16,669
LATCH.cache buffers chains 46,147 1,999 -44,148
STAT...session uga memory 189,012 65,464 -123,548
STAT...session pga memory 196,608 65,536 -131,072
STAT...session pga memory max 262,144 65,536 -196,608
STAT...session uga memory max 315,052 65,560 -249,492
Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
49,380 2,264 -47,116 2,181.10%
二者的差異同樣很明顯。
不僅如此,在儲存過程中,執行P1過程之後,甚至在臨時表空間中產生了臨時段,而這個臨時段是不會自動清除的,經測試,即使沒有用於返回結果的clob引數,在過程內部生成的clob所佔的臨時段也不會自動清除,只有會話退出才會清除掉。如果更深入跟蹤,也許可以發現大量的邏輯讀來源於這個臨時段。
SQL> select tablespace,contents,segtype,blocks from v$sort_usage;
TABLESPACE CONTENTS SEGTYPE BLOCKS
------------------------------- --------- --------- ----------
TEMP TEMPORARY LOB_DATA 128
透過驗證,證明clob型別的資料的確是引起客戶系統中儲存過程大量邏輯讀和Latch爭用的原因,找到了這個原因,最佳化就相對簡單了。
在PL/SQL儲存過程中,clob是相當方便的一種資料型別,由於其能夠儲存超長字元資料的特性,使得在這種用於資料交換的儲存過程中用得較多。然而,從上面的資料中,不難發現,如果大量使用clob運算,將普通的字串拼接成clob,其CPU消耗、邏輯讀、甚至是latch的獲取都是非常高的,對效能影響非常大。實際上在10g中,儲存過程中的varchar2型別,其長度最大可以達到32767,所以如果返回的結果確保不超過這個長度,完全可以使用varchar2型別,只是在返回時再轉換為clob,正如上面的儲存過程P2所做的那樣。如果其長度超過了32767,也可以如儲存過程P3一樣,先將短小的字串拼接成較大的字串,然後將較大的字串拼接到clob中。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/18922393/viewspace-708493/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Oracle中Clob型別處理解析Oracle型別
- Oracle 中LONG RAW BLOB CLOB型別介紹Oracle型別
- Oracle Long型別轉換為Clob型別Oracle型別
- 【開發篇plsql】plsql資料型別(一) 集合型別SQL資料型別
- java jdbc存取oracle clob型別JavaJDBCOracle型別
- 【開發篇plsql】plsql物件型別SQL物件型別
- plsql記錄型別SQL型別
- LONG欄位型別向CLOB遷移型別
- 利用PLSQL包載入CLOB欄位SQL
- 【開發篇plsql】plsql資料型別(二) recordSQL資料型別
- PLSQL學習——資料型別SQL資料型別
- JDBC 處理CLob和Blob型別資料JDBC型別
- SQL Server中text型別匯入oracle clob欄位時的設定SQLServer型別Oracle
- java語言操作Oracle資料庫中的CLOB資料型別 (轉)JavaOracle資料庫資料型別
- [20160624]慎用nvarchar2資料型別.txt資料型別
- oracle如何變更varchar2型別的列為clobOracle型別
- PLSQL Language Reference-BOOLEAN型別-預定義PLS_INTEGER子型別SQLBoolean型別
- PLSQL Language Reference-PL/SQL資料型別-SQL資料型別-有限制的子型別SQL資料型別
- PLSQL學習-【4複合資料型別】SQL資料型別
- PLSQL Language Referenc-SQL資料型別-子型別與基型別具有相同的資料型別家族SQL資料型別
- 加強型WM_CONCAT2函式(不受32767限制的CLOB型別)函式型別
- 保留資料庫表中的資料,把表中的欄位varchar2改成clob型別資料庫型別
- 【SQL】Oracle建立CLOB型別上傳下載讀取檔案SQLOracle型別
- 10g PLSQL數值型別的溢位SQL型別
- PLSQL Language Reference-SQL資料型別-PLS_INTEGER的子型別SIMPLE_INTEGERSQL資料型別
- PLSQL Language Reference-BOOLEAN型別-PLS_INTEGER和BINARY_INTEGER資料型別SQLBoolean資料型別
- 測試TOM=用PLSQL載入LOB型別資料SQL型別
- PL/SQL基本結構---PLSQL複合型別---表型別變數tableSQL型別變數
- 如何將varchar2修改為clob型別欄位(使用long過渡)型別
- PLSQL Language Reference-PL/SQL資料型別-SQL資料型別-不同的最值大小SQL資料型別
- PLSQL Language Reference-PL/SQL資料型別-SQL資料型別-使用者定義的PL/SQL子型別SQL資料型別
- PLSQL Language Referenc-PL/SQL集合和記錄-集合型別SQL型別
- PLSQL Language Reference-PL/SQL資料型別-SQL資料型別-LONG和LONG RAW變數SQL資料型別變數
- [20231013]CLOB型別的編碼問題.txt型別
- PLSQL Language Reference-PL/SQL資料型別-SQL資料型別-ROWID和UROWID變數SQL資料型別變數
- PLSQL Language Reference-PL/SQL資料型別-SQL資料型別-CHAR和VARCHAR2變數SQL資料型別變數
- vue中慎用style的scoped屬性Vue
- 儲存過程中慎用 execute immediate儲存過程