Oracle 11g中Temp臨時表空間、文件的新特性
html
臨時表空間是Oracle體系結構中比較特殊的結構。一般情境下,數據庫使用者只須要設置對應的臨時表空間(到用戶),臨時段分配等工做都是系統自動完成。當臨時數據不須要時,Oracle後臺進程SMON也會負責將臨時段回收。算法
在Oracle的備份恢復體系中,臨時文件的地位比較低。在進行備份動做時,RMAN都不會進行臨時文件恢復。在恢復啓動過程當中,若是發現臨時文件不存在,一般Oracle也會自動將臨時文件建立出來。sql
一、Temp漫談數據庫
Oracle臨時表空間主要充當兩個主要做用:臨時表數據段分配和排序彙總溢出段。咱們建立的臨時表,在使用過程當中,會有大量的數據段結構的分配。這個分配就是利用臨時表空間。c#
排序彙總溢出的範圍比較普遍。咱們在SQL語句中進行order by/group by等操做,首先是選擇PGA的內存sort area、hash area和bitmap area。若是SQL使用排序空間很高,單個server process對應的PGA不足以支撐排序要求的時候,臨時表空間會充當排序段的數據寫入。這樣排序動做會從內存過程退化爲外存儲過程。session
兩個現象:若是咱們的Temp表空間文件設置比較小,而且設置爲不可自動拓展。同時咱們又但願給一個很大數據表加索引,常常會遇到:create index語句長時間運行以後報錯,說Temp表空間不能拓展,操做被中止。索引葉子節點是有序的,建立索引的過程也就伴隨着數據庫的排序動做。架構
另外一個現象:若是咱們的內存設置不合理,SQL常常包括不少「無心義」的「大排序」。這樣會發現咱們的Temp空間消耗比較大,一些SQL性能抖動比較明顯。併發
合理的設置Temp空間管理策略,是應用系統架構的一個重要環節。oracle
二、給臨時表指定表空間app
Oracle中,用戶schema和表空間存儲結構對應關係是很靈活的。若是用戶有空間配額(Quota),咱們是能夠在schema中建立任何表空間的數據表的,是能夠把對象放置在任何的表空間裏面。
可是對於11g以前,Temp表空間並非這樣的。咱們建立用戶以後,須要制定出這個用戶schema對應的臨時表空間。若是咱們不指定,Oracle會選擇系統默認臨時表空間(一般是temp)做爲這個用戶的臨時表空間。
至此之後,這個用戶全部的臨時段都是在這個臨時表空間上進行分配。咱們是沒有能力指定某個臨時表分配在其餘臨時表空間裏面的。
咱們到11g以後,Oracle提供了這樣的自由。
SQL> select * from v$version;
BANNER
-----------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 – Production
當前sys用戶的默認表空間爲TEMP。
SQL> select DEFAULT_TABLESPACE, TEMPORARY_TABLESPACE from dba_users where username='SYS';
DEFAULT_TABLESPACE TEMPORARY_TABLESPACE
------------------------------ ------------------------------
SYSTEM TEMP
此時,數據庫中包括兩個臨時表空間。
SQL> select * from dba_temp_free_space;
TABLESPACE_NAME TABLESPACE_SIZE ALLOCATED_SPACE FREE_SPACE
--------------- --------------- --------------- ----------
TEMP 481296384 481296384 480247808
TEMPTEST 5368709120 1048576 5367660544
咱們能夠建立出一個不屬於TEMP默認臨時表空間的臨時表。
SQL> create global temporary table t_temp tablespace temptest as select * from t where 1=0;
Table created
此後的臨時段分配,都是在temptest表空間上進行的。
SQL> insert into t_temp select * from t;
19360512 rows inserted
SQL> select * from dba_temp_free_space;
TABLESPACE_NAME TABLESPACE_SIZE ALLOCATED_SPACE FREE_SPACE
--------------- --------------- --------------- ----------
TEMP 481296384 481296384 480247808
TEMPTEST 5368709120 2248146944 3120562176
那麼,是否是和數據表同樣,支持move操做呢?
SQL> create global temporary table t_temp tablespace temp as select * from dba_objects where 1=0;
create global temporary table t_temp tablespace temp as select * from dba_objects where 1=0
ORA-14451: unsupported feature with temporary table
看來,目前版本尚未支持move操做的臨時表。
三、臨時表空間、文件的shrink
臨時表空間是不會有持久化數據保存的。因此,不少被「脹大」的表空間都存在一個shrink收縮問題。從11g開始,Oracle支持Temp表空間和臨時文件的搜索方法。
爲了進行試驗,咱們先向使用表空間Temptest添加文件。
SQL> alter tablespace temptest add tempfile size 1G;
Tablespace altered
SQL> select file_name, file_id, tablespace_name from dba_temp_files;
FILE_NAME FILE_ID TABLESPACE_NAME
------------------------------ ---------- ---------------
/u01/app/oradata/ORA11G/datafi 1 TEMP
le/o1_mf_temp_92t73qm8_.tmp
/u01/app/oradata/ORA11G/datafi 2 TEMPTEST
le/o1_mf_temptest_9j80859z_.tm
p
/u01/app/oradata/ORA11G/datafi 3 TEMPTEST
le/o1_mf_temptest_9j826c9b_.tm
p
空間狀況:
SQL> select * from dba_temp_free_space;
TABLESPACE_NAME TABLESPACE_SIZE ALLOCATED_SPACE FREE_SPACE
--------------- --------------- --------------- ----------
TEMP 481296384 481296384 480247808
TEMPTEST 6442450944 2249195520 6440353792
新加入臨時文件到臨時表空間,因爲文件採用稀疏文件結構,因此咱們allocated_space沒有增長,而free_space有增長。
磁盤空間也不會變化。
[root@SimpleLinux ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 48G 26G 20G 57% /
tmpfs 6.0G 256M 5.8G 5% /dev/shm
/dev/mapper/VolGrp01-lv1
194M 5.6M 179M 4% /voltest01
[root@SimpleLinux ORA11G]# cd datafile/
[root@SimpleLinux datafile]# ls -l | grep temptest
-rw-r----- 1 oracle oinstall 5368717312 Feb 19 09:10 o1_mf_temptest_9j80859z_.tmp
-rw-r----- 1 oracle oinstall 1073750016 Feb 19 09:28 o1_mf_temptest_9j826c9b_.tmp
咱們能夠直接使用shrink tempfile的方法,將文件限制大小。Keep字句中包括控制大小。
SQL> alter tablespace temptest shrink tempfile '/u01/app/oradata/ORA11G/datafile/o1_mf_temptest_9j826c9b_.tmp' keep 500m;
文件系統中,文件顯示出的大小便爲500M,可是磁盤分配沒有變化,由於歷來就沒有分配過。
[root@SimpleLinux datafile]# ls -l | grep temptest
-rw-r----- 1 oracle oinstall 5368717312 Feb 19 09:10 o1_mf_temptest_9j80859z_.tmp
-rw-r----- 1 oracle oinstall 525336576 Feb 19 09:35 o1_mf_temptest_9j826c9b_.tmp
[root@SimpleLinux datafile]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 48G 26G 20G 57% /
tmpfs 6.0G 256M 5.8G 5% /dev/shm
/dev/mapper/VolGrp01-lv1
194M 5.6M 179M 4% /voltest01
縮小的500M,在dba_temp_free_space中有所表現。
SQL> select * from dba_temp_free_space;
TABLESPACE_NAME TABLESPACE_SIZE ALLOCATED_SPACE FREE_SPACE
--------------- --------------- --------------- ----------
TEMP 481296384 481296384 480247808
TEMPTEST 5894037504 2249187328 5891948544
若是咱們對那個已經分配的臨時文件進行shrink,是會影響到磁盤結構的。
SQL> alter tablespace temptest shrink tempfile '/u01/app/oradata/ORA11G/datafile/o1_mf_temptest_9j80859z_.tmp' keep 1G;
Tablespace altered (長時間執行)
[root@SimpleLinux datafile]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 48G 25G 21G 55% /
tmpfs 6.0G 256M 5.8G 5% /dev/shm
/dev/mapper/VolGrp01-lv1
194M 5.6M 179M 4% /voltest01
SQL> select * from dba_temp_free_space;
TABLESPACE_NAME TABLESPACE_SIZE ALLOCATED_SPACE FREE_SPACE
--------------- --------------- --------------- ----------
TEMP 481296384 481296384 480247808
TEMPTEST 1600110592 2080768 1598029824
此外,咱們也是能夠對Temp表空間直接進行shrink過程。
SQL> create temporary tablespace temptest tempfile size 1G
2 extent management local uniform size 1m;
Tablespace created
SQL> alter tablespace temptest shrink space keep 500m;
Tablespace altered
此時檢查視圖dba_temp_free_space。
SQL> select * from dba_temp_free_space;
TABLESPACE_NAME TABLESPACE_SIZE ALLOCATED_SPACE FREE_SPACE
------------------------------ --------------- --------------- ----------
TEMP 30408704 30408704 29360128
TEMPTEST 525336576 1048576 524288000
影響到的就是表空間總大小。若是咱們不指定keep,Oracle會將表空間縮小到元數據階段。
SQL> alter tablespace temptest shrink space;
Tablespace altered
SQL> select * from dba_temp_free_space;
TABLESPACE_NAME TABLESPACE_SIZE ALLOCATED_SPACE FREE_SPACE
------------------------------ --------------- --------------- ----------
TEMP 30408704 30408704 29360128
TEMPTEST 2088960 1040384 1048576
肯定了2M大小,1M是分配元數據。
四、結論
11g中提供了不少臨時表空間操做的特性,這幫助咱們更好的管理和控制臨時表空間。
一個電信運營商客戶的核心交易系統,臨時表空間大量被佔用,臨時表空間被撐到了600GB。這樣的問題複雜嗎?取決於不少因素,不過今天所要講的案例,並不複雜,若是咱們對臨時表空間在何種狀況下使用有足夠了解。
首先,咱們要去檢查是什麼會話佔用了臨時表空間,具體佔用了多少,臨時段的具體類型是什麼。正如咱們要想知道這個月的花費過大,去分析緣由時就要去看是哪些開銷過大、開銷了多少金額、開銷的用途等。
這個步驟比較簡單,查詢v$sort_usage就能夠了:
view plain copy to clipboard print ?
SQL_ID都是同樣的,那這個SQL是否有其特殊性呢?SEGTYPE爲SORT代表這個臨時段是「排序段」,用於SQL排序,大小竟然也是同樣,會話佔用的臨時段大小將近1GB,幾百個會話加在一塊兒,想不讓臨時表空間不撐大都難。
看看這個相同的SQL ID表明的SQL是什麼:
view plain copy to clipboard print ?
很明顯,這是一條很是簡單的SQL,沒有ORDER BY ,也沒有GROUP BY、UNION、DISTINCT等須要排序的,TABLE_XXX是一張普通的表,而不是視圖。出現了什麼問題?會不會是v$sort_usage的SQL_ID列有錯誤?咱們查看其中一個會話正在執行的SQL:
view plain copy to clipboard print ?
v$sort_usage中看到某個會話當前沒有執行任何SQL,v$sort_usage中的SQL_ID是該會話前一條執行的SQL。爲何這裏顯示的是會話前一條執行的SQL,關於這個問題後面再詳述,但至少有一點是能夠判斷的:若是大量的臨時段都是由會話當前正在執行的SQL所產生的,那說明同時有幾百個會話在執行須要大量臨時空間的SQL,那系統早就崩潰了。因此這些臨時表空間的佔用不該該是由當前在執行的SQL所產生的,至少大部分不是。
大部分人的一個錯誤觀點是,臨時表空間中當前佔用的空間是由會話當前正在執行的SQL所產生的。上面的一個簡單的分析判斷,狀況不該該是這樣。咱們能夠基於查詢類SQL的執行過程來分析:
關鍵在第3步。你們都知道取數據有一個array size的概念,表示一次從遊標中取多少條數據,這是一個循環的過程。若是SQL查詢獲得的數據有1000條,每次取100條,則須要取10次。對於Fetch Cursor,有兩點:
很顯然,從上述第2點能夠知道,若是一條SQL使用了臨時段來排序,在SQL對應的遊標沒關閉的狀況下,Oracle數據庫不會去釋放臨時段,由於對於Oracle數據庫來講,它不會知道客戶端是否還要繼續取遊標的數據。
基於這樣的分析,咱們只須要隨便選擇一個佔用了接近1GB的會話,查詢v$open_cursor,查看其打開的遊標中是否有大數據量排序的SQL:
view plain copy to clipboard print ?
最後三個遊標,實際上都是同一條SQL語句,排序的數據量最大,咱們來看看這條SQL是什麼:
view plain copy to clipboard print ?
基於爲客戶保密的緣由,SQL作了處理,能知道這條SQL的確是排了序就行,不過在SQL中看不出來的是,這條SQL沒有任何實質性的可以過濾大量數據的條件。那麼咱們count(*)這條SQL語句看看:
view plain copy to clipboard print ?
出來的結果竟然有1200多萬條數據,一個前臺應用,不知道取1200多萬條數據幹嗎。可是從rows_processed/executions只有幾萬的結果來看,應用在取了幾萬條數據以後,因爲某些緣由(最大的可能就是不能再處理更多的數據),再也不繼續取數據,可是遊標也一直沒有關閉。
比較容易就能進行演示sort by時臨時表空間的佔用。
view plain copy to clipboard print ?
問題分析到這裏,很明顯確認的是,應用存在問題,也許是業務邏輯問題;也許是根據前臺選擇的條件拼接的SQL,可是沒有任何條件時就查詢了全部數據。接下來就是找來開發人員,至於後面的事就跟這個主題沒有太大關係。咱們能夠根據這個案例來進一步展開,去探尋臨時表空間的更多知識點。
這裏要展開的第1點是,v$sort_usage中的sql_id是否是會話正在執行的SQL,咱們去看看視圖fixed_View_definition就知道了:
view plain copy to clipboard print ?
原來在v$sort_usage的定義中,就明確地說明了SQL_ID列是v$session中的prev_sql_id列,而不是當前的SQL。至於爲何這樣定義,老實說,如今還不知道。
不過從11.2.0.2這個版本開始,v$sort_usage的基表x$ktsso中增長了一個字段ktssosqlid,表示該臨時段真正關聯的SQL,以上述的測試結果爲例,查詢這個基表的結果以下:
view plain copy to clipboard print ?
能夠看到的是咱們查詢到了真正產生臨時段的SQL。
一直以來,v$sort_usage中的SQL_ID誤導了不少人。所幸的是Oracle從11.2.0.2開始進行了彌補,MOS中有文檔:
Bug 17834663 - Include SQL ID for statement that created a temporary segment in GV$SORT_USAGE (文檔 ID 17834663.8)
In previous versions, it was not possible to identify the SQL ID
of the statement that created a given temporary segment in
eg. (G)V$SORT_USAGE.@ Via the fix for bug:8806817 we added the SQL ID to the X$KTSSO
@ table (ktssosqlid), but it was not exposed in the GV$SORT_USAGE
@ view until now.The SQL ID of the statement is in column SQL_ID_TEMPSEG
Note that this fix cannot be provided as an interim patch.
咱們改良一下v$sort_usage,使用以下的查詢來代替:
view plain copy to clipboard print ?
要展開的第2點是,v$sort_usage中的SEGTYPE列的不一樣的值各有什麼意義:
根據上述的段類型,大致能夠分爲三類佔用:
臨時表空間的異常佔用,一種緩步增加的,另外一種狀況:一下撐滿的一般是一個極大數據量的排序或極大的索引的建立。緩步增加的狀況,跟系統的內存被逐漸佔用相似,存在「泄露」。好比排序的SQL遊標沒有關閉,好比本文的案例;好比會話級臨時表產生了數據後一直沒有清除;臨時LOB對象沒有清理或泄露。前兩種比較好去分析處理,可是臨時LOB的泄露問題就複雜不少。
來看一個測試:
view plain copy to clipboard print ?
上述的代碼執行完以後,在另外一個窗口中,咱們查詢v$sort_usage:
view plain copy to clipboard print ?
能夠看到,這個會話已經產生了類型爲LOB_DATA的臨時段。雖然SQL代碼已經執行完成,會話已經處於空閒狀態,可是臨時段仍然存在着。
Oracle中的LOB變量,相似於C語句中的指針,或者相似於JAVA代碼中的數據庫鏈接Connection,是須要釋放的。上述有問題的代碼,缺乏了釋放LOB的代碼:dbms_log.freetemporary(v_lob)。好在對於這種狀況,Oracle提供了一個補救措施,就是設置60025事件能夠自動清理掉不活動的LOB,只須要在參數文件中加上event='60025 trace name context forever'。
在Oracle數據庫中,xmltype類型內部也其實是LOB類型,xmltype類型的數據操做可能會產生較多的LOB臨時段。lob類型的字段上的更改操做,好比lob拼接等,一樣會產生LOB臨時段。若是在v$sort_usage中發現大量的LOB類型的臨時段,那麼一般是因爲代碼存在問題,沒有釋放LOB,或者是因爲Oracle自己的BUG。在MOS上,若是以lob temporary關鍵字搜索,會發現至關多的關於lob臨時段的泄露或臨時段沒有釋放相關的文檔。
最後,無論是什麼狀況致使的臨時表空間被過多佔用,一般重啓應用可以釋放掉臨時段,由於會話退出後,相對應的臨時段就會被釋放。看來,「重啓」大法在這種狀況下就頗有用。
--The END.
Address: http://www.laoxiong.net/temporary_tablespace_excessive_usage_case.html
一、臨時表空間的用途
1)disk sort
在瞭解disk sort排序以前,先來看一段doc(When the WORKAREA_SIZE_POLICY parameter is set to MANUAL, the
maximum amount of memory allocated for a sort is defined by the
parameter SORT_AREA_SIZE. If the sort operation is not able to
completely fit into SORT_AREA_SIZE memory, then the sort is separated into phases.The temporary output of each phase is stored in temporary
segments on disk. The tablespace in which these sort segments are created is the users temporary tablespace.When Oracle writes sort operations to disk, it writes out partially sorted
data in sorted runs. After all the data has been received by the sort, Oracle merges the runs to produce the final sorted output. If the sort area
is not large enough to merge all the runs at once, then subsets of the runs
are merged in several merge passes. If the sort area is larger, then there
are fewer, longer runs produced. A larger sort area also means that the
sort can merge more runs in one merge pass.)
從上面doc看來,我本身的理解是排序始終是在內存裏完成的,若是要排序的數據量很大,在內存裏不能完成,oracle會分階段來排序,每次先排一部分,而且把排好序的數據臨時存放在用戶default temporary tablespace中的temp segment上,而臨時表空間對應的tempfile屬於disk文件,這就是disk sort的由來。具體oracle是如何分階段來排序的,doc說的還算清楚,只是可能不太好理解。其實上面doc說的不正是排序時常提到的3種狀況:optimail,one pass sort,mutli-pass sort
a)optimal
如圖1
咱們知道排序最理想的方式是optimal,也就是須要排序的數據都在內存裏放的下,並且內存有足夠空間作排序。排序自己的原理多是至關複雜的,可是大體的說法應該是排序時在內存須要維護一個樹狀的結構來完成排序,因此假如你有5M的數據須要排序,這時候你須要的內存會遠大於5M。
b)one pass sort
如圖2
假如須要排序的是1到20,但內存一次只能排序5個數據,這時候不得不5個數據作一個排序,每排好一組就放在tempfile上,最後在磁盤上就存在4組數據,這時候If the sort area
is large enough to merge all the runs at once,那麼所作的sort就是one pass的。在這個特定的例子裏,large enough應該是指有能力一次在內存裏作4個數據的排序,及首先在1,2,12,11中可以選出最小的,就是1,而後在接下來的6,2,12,11裏選出2, 而後在6,3,12,11中選出3,以此類推
c)mutli-pass sort
如圖3
在這個例子裏,若是內存小到一次只能排序3個數據,那onepass 就作不到了,按照和onepass相似的方法,先在tempfile獲得7組數據,而後這時候由於the sort area
is not large enough to merge all the runs at once, then subsets of the runs
are merged in several merge passes.
所以須要把7組數據變成第二階段的3組,而後在把這3組數據排序。由於在磁盤上存了2次數據,因此叫multi-pass,內存越小,pass的次數越多,排序須要的時間也就越長。
2)global temporary table
oracle支持兩種類型的臨時表,之因此稱爲global,是由於不管是哪一個session建立的臨時表,該表對全部的session都是可見的,可是數據僅僅對建立臨時表的session可見。
oracle支持兩種類型的臨時表:
a)transaction-specific (the default)
doc中提到的transaction-specific其實就是指在建立臨時表時在語法中指定的on commit delete rows,這種類型的臨時表在commit以後也就是事務結束之後數據被刪除了:
SQL> create global temporary table gt1 as select * from dba_tables;
表已建立。
SQL> select count(*) from gt1;
COUNT(*)
----------
0
b)session-specific
這種臨時表指的就是在建立時使用on commit preseve rows子句建立的臨時表,它的特色是在事務結束之後數據並無當即被刪除,而是在session結束以後數據被刪除的:
SQL> create global temporary table gt3 on commit preserve rows as select * from
dba_tables;
表已建立。
SQL> select count(*) from gt3;
COUNT(*)
----------
1212
在另外一個sqlplus窗口看看數據發現不可見:
SQL> select count(*) from gt3;
COUNT(*)
----------
0
SQL>
再回到建立gt3時的窗口:
SQL> insert into gt3 select *from dba_tables;
已建立1213行。
SQL> select count(*) from gt3;
COUNT(*)
----------
2425
SQL> rollback;
回退已完成。
SQL> select count(*) from gt3;
COUNT(*)
----------
1212
SQL> truncate table gt3;
表被截斷。
SQL> select count(*) from gt3;
COUNT(*)
----------
0
SQL>
試驗發現global temporary table支持rollback,意味着對temporary table的操做是須要佔用undo的。而undo是須要保護的,所以使用臨時表也會產生undo生成的少許redo。臨時表也支持truncate,而doc上說的(DDL operations (except TRUNCATE) are allowed on an existing temporary table only if no session is currently bound to that temporary table)有些莫名其妙。
咱們在判斷臨時表究竟是屬於那種類型時除了經過數據驗證以外,也能夠查詢數據字典得到:
SQL> select TABLE_NAME ,TEMPORARY , DURATION,tablespace_name,logging from dba_t
ables where table_name in ('GT1','GT2','GT3');
TABLE_NAME T DURATION TABLESPACE LOG
------------------------------ - --------------- ---------- ---
GT3 Y SYS$SESSION NO
GT2 Y SYS$TRANSACTION NO
GT1 Y SYS$TRANSACTION NO
DURATION字段說明了臨時表的類型。
退出建立gt3時的session發現數據被清除了:
SQL> insert into gt3 select *from dba_tables;
已建立1213行。
SQL> commit;
提交完成。
SQL> select count(*) from gt3;
COUNT(*)
----------
1213
SQL> disconnect
從 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options 斷開
SQL> connect sys/system@dmt as sysdba
已鏈接。
SQL> select count(*) from gt3;
COUNT(*)
----------
0
臨時表和普通的heap表產生的日誌對比:
SQL> create table t1 tablespace users as select * from dba_objects where 1=2;
表已建立。
SQL> SELECT b.name , a.value FROM v$sysstat a ,
2 v$statname b
3 where a.statistic#=b.statistic#
4 and b.name like 'redo size%';
NAME VALUE
---------------------------------------------------------------- ----------
redo size 3248648
SQL> insert into t1 select * from dba_objects;
已建立11260行。
SQL> commit;
提交完成。
SQL> SELECT b.name , a.value FROM v$sysstat a ,
2 v$statname b
3 where a.statistic#=b.statistic#
4 and b.name like 'redo size%';
NAME VALUE
---------------------------------------------------------------- ----------
redo size 4780384
SQL> select 4780384 - 3248648 from dual;
4780384-3248648
---------------
1531736
再來看看一樣的操做臨時表產生的reodo:
SQL> create global temporary table gt1 on commit preserve rows as select * from
dba_objects where 1=2;
表已建立。
SQL> select b.name , a.value from v$sysstat a , v$statname b where a.statistic#=
b.statistic# and b.name like '%redo size%';
NAME VALUE
---------------------------------------------------------------- ----------
redo size 1584124
SQL> insert into gt1 select * from dba_objects;
已建立11456行。
SQL> commit;
提交完成。
SQL> select b.name , a.value from v$sysstat a , v$statname b where a.statistic#=
b.statistic# and b.name like '%redo size%';
NAME VALUE
---------------------------------------------------------------- ----------
redo size 1773124
SQL> select 1773124 - 1584124 from dual;
1773124-1584124
---------------
189000
相差的redo:
SQL> select 1531736 - 189000 from dual;
1531736-189000
--------------
1342736
SQL>
經過上面對比咱們發現一樣的操做使用臨時表比普通的表要少產生不少日誌;而儘管臨時表顯示的是nologging,可是仍是產生了一些日誌,這些日誌實際上是由undo 產生的,由於臨時表支持rollback,意味着對臨時表執行dml操做是須要佔用undo的,而undo自己也是須要保護的,所以對undo的使用產生了一部分redo。
另外在建立臨時表時不能指定tablespace tablespace_name子句,臨時表只能使用用戶的default temporary tablespace:
SQL> create global temporary table gt2(id int) on commit preserve rows tablespac
e tmp;
create global temporary table gt2(id int) on commit preserve rows tablespace tmp
*
第 1 行出現錯誤:
ORA-14451: 不受支持的臨時表功能
瞭解了臨時表的種類以及臨時表的特性以後就能夠根據須要靈活的使用臨時表。
二、臨時表空間的分類
上面提到disk sort會使用臨時表空間,根據這個用途,臨時表空間能夠分爲下面3種:
1)Permanent(其實就是指system表空間)
在dmt下,若是沒有爲系統建立Tablespaces of Type TEMPORARY 類型的表空間,固然若是也不存在lmt管理的temporary tablespace,此時系統在disk sort時會使用system表空間,而system表空間屬於Permanent(永久)表空間,使用時會產生redo。簡單的作個測試:
SQL> select tablespace_name , extent_management,contents,logging from dba_tables
paces;
TABLESPACE EXTENT_MAN CONTENTS LOGGING
---------- ---------- --------- ---------
SYSTEM DICTIONARY PERMANENT LOGGING
UNDOTBS1 LOCAL UNDO LOGGING
SYSAUX LOCAL PERMANENT LOGGING
TEMP LOCAL TEMPORARY NOLOGGING
USERS LOCAL PERMANENT LOGGING
10g引入了default temporary tablespace,default temporary tablespace不能刪除,除非再指定一個temporary tablespace爲default的或者system也能夠做爲default temporary tablespace
SQL> drop tablespace temp including contents and datafiles;
drop tablespace temp including contents and datafiles
*
第 1 行出現錯誤:
ORA-12906: 不能刪除默認的臨時表空間
SQL> alter database default temporary tablespace system;
數據庫已更改。
除了system表空間以外沒有哪一種Permanent表空間能夠做爲臨時表空間用
SQL> alter database default temporary tablespace sysaux;
alter database default temporary tablespace sysaux
*
第 1 行出現錯誤:
ORA-12902: 默認的臨時表空間必須屬 SYSTEM 或 TEMPORARY 類型
SQL> drop tablespace temp including contents and datafiles;
表空間已刪除。
SQL> select tablespace_name , extent_management,contents,logging from dba_tables
paces;
TABLESPACE EXTENT_MAN CONTENTS LOGGING
---------- ---------- --------- ---------
SYSTEM DICTIONARY PERMANENT LOGGING
UNDOTBS1 LOCAL UNDO LOGGING
SYSAUX LOCAL PERMANENT LOGGING
USERS LOCAL PERMANENT LOGGING
SQL> SELECT b.name , a.value FROM v$sysstat a ,
2 v$statname b
3 where a.statistic#=b.statistic#
4 and b.name like 'redo size%';
NAME VALUE
---------------------------------------------------------------- ----------
redo size 110271220
SQL> select group# , status , sequence# from v$log;
GROUP# STATUS SEQUENCE#
---------- ---------------- ----------
1 INACTIVE 130
2 CURRENT 131
3 INACTIVE 129
對t表(t是一個具備72w數據大約80m左右的表)進行排序:
select * from t order by object_id desc
SQL> select group# , status , sequence# from v$log;
GROUP# STATUS SEQUENCE#
---------- ---------------- ----------
1 INACTIVE 130
2 ACTIVE 131
3 CURRENT 132
SQL> SELECT b.name , a.value FROM v$sysstat a ,
2 v$statname b
3 where a.statistic#=b.statistic#
4 and b.name like 'redo size%';
NAME VALUE
---------------------------------------------------------------- ----------
redo size 113859320
SQL> select (113859320 - 110271220)/1024/1024 from dual;
(113859320-110271220)/1024/1024
-------------------------------
3.42187881
咱們發現日誌進行了切換,並且生成了大約3.5m的redo。
查看disk sort使用sytem 表空間的狀況:
SQL> select policy ,work_area_size , actual_mem_used , number_passes , tempseg_s
ize , tablespace from v$sql_workarea_active;
POLICY WORK_AREA_SIZE ACTUAL_MEM_USED NUMBER_PASSES TEMPSEG_SIZE TABLES
------------ -------------- --------------- ------------- ------------ ------
AUTO 2412544 2675712 1 86833152 SYSTEM
清楚的看到使用了system表空間進行了disk sort,使用Permanent tablespaces(就是指system表空間)進行disk sort排序的缺點是:
Permanent tablespaces (which are not of type TEMPORARY) are least efficient for performance of disk sorts. This is because of the following reasons:
The ST-enqueue is used for allocation and de-allocation of each extent allocated to a sort segment.
Sort-segments are not reused. Each process performing a disk sort creates then drops it's own sort segment. In addition, a single sort operation can require the allocation and deallocation of many extents, and each extent allocation requires the ST-enqueue.
簡單的驗證一下sort segment的分配和回收狀況:
排序時system的空間使用狀況:
SQL> select round((a.bytes - b.bytes)/a.bytes*100,2) used_space_pct
2 from dba_data_files a, dba_free_space b
3 where a.file_id = b.file_id
4 and a.file_id=1;
USED_SPACE_PCT
--------------
98.57
排序後system的空間使用狀況:
SQL> select round((a.bytes - b.bytes)/a.bytes*100,2) used_space_pct
2 from dba_data_files a, dba_free_space b
3 where a.file_id = b.file_id
4 and a.file_id=1;
USED_SPACE_PCT
--------------
70.96
也就是說排序以後sort segment當即進行了釋放,而在爲sort segment分配和回收extents都會產生ST-enqueue ( Space transaction enqueue),這一點能夠在排序時觀察到,通常不太容易觀察到:
SQL> select * from v$lock where type='ST';
ADDR KADDR SID TY ID1 ID2 LMODE REQUEST
CTIME BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ----
------ ----------
6D22DDE4 6D22DDF8 26 ST 0 0 6 0
0 0
另外使用system表空間進行disk sort時的temp segment是在排序時建立的,而排序以後當即進行了刪除,也就是說這個排序段不能重複利用。簡單作個測試:
SQL> select segment_name , segment_type , tablespace_name , header_file,header_b
lock , blocks , extents from dba_segments where segment_type like upper('%temp%'
);
未選定行
--在plsql developer對錶t實行排序:
select * from t order by object_id desc ;
SQL> select segment_name , segment_type , tablespace_name , header_file,header_b
lock , blocks , extents from dba_segments where segment_type like upper('%temp%'
);
SEGMENT_NA SEGMENT_TY TABLESPACE HEADER_FILE HEADER_BLOCK BLOCKS EXTENTS
---------- ---------- ---------- ----------- ------------ ---------- ----------
1.109061 TEMPORARY SYSTEM 1 109061 95136 19
SQL> select segment_name , segment_type , tablespace_name , header_file,header_b
lock , blocks , extents from dba_segments where segment_type like upper('%temp%'
);
未選定行
SQL>
對比排序先後temp segment的狀況,顯然temp segment是在排序時建立的,排序以後當即釋放了,若是下次須要排序,還須要從新分配extent,從新建立temp segment,而分配或者回收extnet都會產生ST-equence.而ST-equence正是dmt的缺陷。
2)Tablespaces of Type TEMPORARY
在沒有lmt以前oracle沒有tempfile的概念,所以建立temp tbs的語法是:
SQL> create tablespace temp datafile 'E:ORACLEPRODUCT10.2.0ORADATADMTTEMP0
1.DBF' SIZE 10M AUTOEXTEND ON TEMPORARY;
表空間已建立。
在10g環境經過上面語法建立的表空間temp的extent管理方式是dictionary,而segment space management 是auto,這顯然是有問題的;而它的contents是TEMPORARY,說明它是臨時表空間,同時也要注意是logging,意味着使用它也要產生redo
SQL> select tablespace_name,contents , logging , extent_management,segment_space
_management from dba_tablespaces;
TABLESPACE CONTENTS LOGGING EXTENT_MAN SEGMEN
---------- --------- --------- ---------- ------
SYSTEM PERMANENT LOGGING DICTIONARY MANUAL
UNDOTBS1 UNDO LOGGING LOCAL MANUAL
SYSAUX PERMANENT LOGGING LOCAL AUTO
TEMP TEMPORARY LOGGING DICTIONARY AUTO
USERS PERMANENT LOGGING LOCAL AUTO
SQL> alter database default temporary tablespace temp;
數據庫已更改。
SQL> select * from t order by object_id desc ;
select * from t order by object_id desc
*
第 1 行出現錯誤:
ORA-10615: Invalid tablespace type for temporary tablespace
出現錯誤的緣由在於:在dmt下不可能存在segment space management auto的tbs,這應該算是10g orace的一個漏洞吧,儘管在10g下使用dmt已經不多了,而使用這種方式建立的temp tbs就更少了,可是應該瞭解oracle的歷史產物。
SQL> alter database default temporary tablespace system;
數據庫已更改。
SQL> drop tablespace temp ;
表空間已刪除。
從新建立一下臨時表空間,指定爲segment space management manual:
SQL> create tablespace temp datafile 'E:ORACLEPRODUCT10.2.0ORADATADMTTEMP0
1.DBF' SIZE 10M reuse AUTOEXTEND ON TEMPORARY segment space management manual;
表空間已建立。
SQL> alter database default temporary tablespace temp;
數據庫已更改。
--再次在plsql developer執行以下語句發現沒有錯誤
select * from t order by object_id desc
而在排序的過程當中發現日誌組進行了很是頻繁的切換,意味着在排序期間產生了大量的redo,並且建立了temp segment:
SQL> select status , group# from v$log;
STATUS GROUP#
---------------- ----------
ACTIVE 1
CURRENT 2
ACTIVE 3
SQL> select status , group# from v$log;
STATUS GROUP#
---------------- ----------
ACTIVE 1
ACTIVE 2
CURRENT 3
SQL> select segment_name , segment_type , tablespace_name , header_file,header_b
lock , blocks , extents from dba_segments where segment_type like upper('%temp%'
);
SEGMENT_NA SEGMENT_TY TABLESPACE HEADER_FILE HEADER_BLOCK BLOCKS EXTENTS
---------- ---------- ---------- ----------- ------------ ---------- ----------
5.2 TEMPORARY TEMP 5 2 78419 15683
SQL> select status , group# from v$log;
STATUS GROUP#
---------------- ----------
CURRENT 1
ACTIVE 2
ACTIVE 3
重啓實例以後發現排序使用的臨時段被刪除了,這種表空間的特色是臨時段被重建以後能夠一直被重用,直到實例重啓以後臨時段被刪除。
SQL> shutdown immediate
數據庫已經關閉。
已經卸載數據庫。
ORACLE 例程已經關閉。
SQL> startup
ORACLE 例程已經啓動。
Total System Global Area 167772160 bytes
Fixed Size 1247900 bytes
Variable Size 71304548 bytes
Database Buffers 92274688 bytes
Redo Buffers 2945024 bytes
數據庫裝載完畢。
數據庫已經打開。
SQL> select segment_name , segment_type , tablespace_name , header_file,header_b
lock , blocks , extents from dba_segments where segment_type like upper('%temp%'
);
未選定行
3)Temporary Tablespaces
使用"Tablespaces of Type TEMPORARY"這種類型的臨時表空間產生的temp segment的缺點是除了dmt在分配和回收extent所存在的ST-equence問題以外,另外一個顯而易見的缺點是在實例重啓以後temp segment也被drop掉了,下次使用temp segment時還須要建立,也就是說temp segment也沒有被很好的重複利用。在lmt出現以後,oracle引入了lmt管理方式的臨時表空間,它的建立語法隨之也發生了變化:
SQL> create temporary tablespace tmp tempfile 'E:ORACLEPRODUCT10.2.0ORADATA
DMTtmp01.dbf' size 10m autoextend on;
表空間已建立。
從建立語法上看首先明確指出了表空間的類型是temporary ;其次引入了tempfile,再也不是datafile了:
SQL> select tablespace_name,contents , logging,extent_management,segment_space_m
anagement,allocation_type from dba_tablespaces where tablespace_name like 'T%';
TABLESPACE CONTENTS LOGGING EXTENT_MAN SEGMEN ALLOCATIO
---------- --------- --------- ---------- ------ ---------
TEMP TEMPORARY LOGGING DICTIONARY MANUAL USER
TMP TEMPORARY NOLOGGING LOCAL MANUAL UNIFORM
還有一個須要說明的地方是在lmt下建立的這種類型的臨時表空間分配extent時只能是uniform而不能是咱們熟悉的autoallocate,default的建立語法是uniform,並且size 是1m。而另外兩個系統用的表空間system,undo只能是autoallocate。
SQL> create temporary tablespace tmp1 tempfile 'E:ORACLEPRODUCT10.2.0ORADATA
DMTtmp02.dbf' size 10m autoextend on uniform size 2m;
表空間已建立。
SQL> create temporary tablespace tmp2 tempfile 'E:ORACLEPRODUCT10.2.0ORADATA
DMTtmp03.dbf' size 10m autoextend on autoallocate;
create temporary tablespace tmp2 tempfile 'E:ORACLEPRODUCT10.2.0ORADATADMT
tmp03.dbf' size 10m autoextend on autoallocate
*
第 1 行出現錯誤:
ORA-25139: CREATE TEMPORARY TABLESPACE 的選項無效
--設置tmp爲default temporary tablespace,doc上提到的temporary
tablespace一旦建立,該表空間中就被建立了一個temp segment,並且該臨時段一直存在而且能夠重複利用,實例重啓以後也不會被刪除直到臨時表空間被刪除它也隨之被刪除,可是彷佛不能很好的驗證doc的說法,由於在建立tmp表空間以後,經過查詢dba_segments並無發現temp segment,並且又作了一個disk sort,依然沒有在dba_segments中發現任何temp segment。oracle經過使用這種臨時表空間所產生的temp segment在dba_segments中彷佛體現不出來了?暫時解釋不清,也是個人疑問!
SQL> alter database default temporary tablespace tmp;
數據庫已更改。
SQL> select segment_name, segment_type, tablespace_name , extents , header_file,
header_block from dba_segments where segment_type like upper('%temp%');
未選定行
SQL> col operation_type format a10
SQL> select operation_type,policy,tempseg_size , tablespace from v$sql_workarea_
active;
OPERATION_ POLICY TEMPSEG_SIZE TABLESPACE
---------- ------------ ------------ ------------------------------
SORT (v2) AUTO 13631488 TMP
SQL>
使用這種臨時表空間的好處是消除了dmt方式下經過不斷的建立、回收temp segment而須要分配或者回收extent所產生的ST-equenue,同時也減小了沒必要要的redo。
三、v$tempseg_usage和v$sort_usage
介紹臨時段不能不提v$tempseg_usage和v$sort_usage,這兩個試圖它們來自相同的數據源,描速的都是臨時段的使用狀況,v$tempseg_usage是oracle從9.2開始引入的一個試圖用來取代v$sort_usage,從試圖的命名理解oracle的用意是用v$tempseg_usage來表述臨時段的使用更爲準確一些,由於畢竟臨時段不只僅是用作disk sort的,因此用v$sort_usage來描速臨時段的使用彷佛不太準確。目前oracle保留v$sort_usage僅僅是爲了向前兼容,因此你們應該更多的使用v$tempseg_usage。
經過v$tempseg_usage咱們能夠很容易的找到那些正經歷着嚴重disk sort的session以及sql(準確的說是使用臨時段嚴重的sql),由於v$tempseg_usage中包含了session以及session正執行的sql的信息。看看v$tempseg_usage的結構就知道包含的這幾個字段一般來講對咱們很是有用:
SESSION_ADDR
RAW(4)
SESSION_NUM
NUMBER
SQLADDR
RAW(4)
SQLHASH
NUMBER
SQL_ID
VARCHAR2(13)
作個簡單的測試:
在plsql developer中執行一個大的排序操做:
select * from t order by object_id desc
在sqlplus窗口中查詢v$tempseg_usage:
SQL> select session_addr,sqladdr,sqlhash,sql_id,tablespace,contents , segtype ,
segfile#
from v$tempseg_usage;
SESSION_ SQLADDR
SQLHASH SQL_ID
TABL CONTENTS
SEGTYPE
SEGFILE#
-------- -------- ---------- ------------- ---- --------- --------- ----------
6D187A0C 66A7747C 4279631818 8dj7zkmzjbzya TEMP TEMPORARY SORT
201
SQL>
這裏經過 session_addr很容易找到排序的session:
SQL> select sid ,serial# , sql_address,sql_hash_value,sql_id , prev_sql_addr,pre
v_hash_value,prev_sql_id from v$session where saddr='6D187A0C';
SID
SERIAL# SQL_ADDR SQL_HASH_VALUE SQL_ID
PREV_SQL
---------- ---------- -------- -------------- ------------- --------
PREV_HASH_VALUE PREV_SQL_ID
--------------- -------------
34
6 66A898C0
4162285281 3n26jhzw1fvr1 66A7B57C
356401299 9m7787camwh4m
經過v$session中提供的sql_id(10g,10g以前能夠經過SQL_ADDR 和SQL_HASH_VALUE 與v$sql作關聯找到正在執行的sql)能夠很容易的找到正在使用臨時段的sql:
SQL> select sql_text from v$sql where sql_id='3n26jhzw1fvr1';
SQL_TEXT
--------------------------------------------------------------------------------
select * from t order by object_id desc
SQL>
遺憾的是經過v$tempseg_usage中的字段SQLADDR
SQLHASH SQL_ID每每不能找到正在使用臨時段的sql,由於v$tempseg_usage中的這幾個字段SQLADDR
SQLHASH SQL_ID
是來自v$session中的PREV_SQL_ADDR PREV_HASH_VALUE PREV_SQL_ID,不知道oracle這樣設計v$tempseg_usage的理由是什麼?爲何不直接包括v$session中的SQLADDR
SQLHASH SQL_ID?
來追溯一下v$tempseg_usage的定義發現原來是V_$SORT_USAGE的同義詞:
SQL> SELECT * FROM DICT WHERE TABLE_NAME IN ('V$SORT_USAGE','V$TEMPSEG_USAGE');
TABLE_NAME
COMMENTS
------------------------------ ------------------------------
V$SORT_USAGE
Synonym for V_$SORT_USAGE
V$TEMPSEG_USAGE
Synonym for V_$SORT_USAGE
SQL>
進一步追蹤看看定義就知道v$tempseg_usage中這幾個字段SQLADDR
SQLHASH SQL_ID原來是來自v$session中的prev_sql_addr, prev_hash_value, prev_sql,這就是咱們經過v$tempseg_usage不能找到正在使用臨時段的sql的真真緣由。
GV$SORT_USAGE的定義:
select x$ktsso.inst_id, username, username, ktssoses, ktssosno, prev_sql_addr, prev_hash_value, prev_sql_id, ktssotsn, decode(ktssocnt, 0, 'PERMANENT', 1, 'TEMPORARY'), decode(ktssosegt, 1, 'SORT', 2, 'HASH', 3, 'DATA', 4, 'INDEX', 5, 'LOB_DATA', 6, 'LOB_INDEX' , 'UNDEFINED'), ktssofno, ktssobno, ktssoexts, ktssoblks, ktssorfno from x$ktsso, v$session where ktssoses = v$session.saddr and ktssosno = v$session.serial#
四、臨時表空間組
臨時表空間組是10g引入的概念,目的是提升同一用戶併發session對臨時段的請求。咱們知道一個臨時表空間中只存在一個臨時段,當一個session在使用臨時段時,其餘session再請求臨時段時須要等到擁有該臨時段的session使用完畢以後才能使用,形成這一問題的根源在於一個用戶只能使用一個臨時表空間。而臨時表空間組的出現大大改善了同一用戶併發session對臨時段的爭奪,由於一個臨時表空間組能夠包括多了臨時表空間,而用戶的default temporary tablespace又能夠是臨時表空間組。其實更直接的說就是臨時表空間組的出現使用戶可以使用多個臨時表空間了。下面作個簡單的測試:
SQL> create temporary tablespace temp tempfile 'E:ORACLEPRODUCT10.2.0ORADATA
DMTtemp01.dbf' size 10m autoextend on;
表空間已建立。
SQL> create temporary tablespace tmp tempfile 'E:ORACLEPRODUCT10.2.0ORADATA
DMTtmp01.dbf' size 10m reuse autoextend on;
表空間已建立。
SQL> select * from dba_tablespace_groups;
未選定行
SQL> alter tablespace temp tablespace group gp1;
表空間已更改。
SQL> alter tablespace tmp tablespace group gp1;
表空間已更改。
SQL> alter database default temporary tablespace gp1;
數據庫已更改。
SQL> select * from dba_tablespace_groups;
GROUP_NAME TABLESPACE_NAME
------------------------------ ------------------------------
GP1 TEMP
GP1 TMP
SQL> alter user xys temporary tablespace gp1;
用戶已更改。
SQL> connect xys/manager@dmt
已鏈接。
SQL> create table t as select * from dba_objects;
表已建立。
SQL> insert into t select * from t;
已建立11260行。
SQL> insert into t select * from t;
已建立22520行。
SQL> insert into t select * from t;
已建立45040行。
SQL> commit;
提交完成。
SQL> create table tt as select * from t;
表已建立。
分別打開兩個plsql developer以用戶xys登陸對錶t和tt同時進行排序,以後經過以下查詢監視對臨時表空間的使用狀況,發現來自同一用戶xys的不一樣session同時排序時使用了同一臨時表空間組內的不一樣臨時表空間,這樣大大減小了以前同一用戶只能使用一個臨時表空間而產生的請求臨時段的等待時間:
SQL> select operation_type ,sql_id , tablespace,tempseg_size,number_passes from
v$sql_workarea_active;
OPERATION_ SQL_ID TABLESPACE TEMPSEG_SIZE NUMBER_PASSES
---------- ------------- ---------- ------------ -------------
SORT (v2) 3n26jhzw1fvr1 TEMP 10485760 1
SORT (v2) 7z5ttxyv6c604 TMP 10485760 1
SQL> select sql_text from v$sql where sql_id in (select sql_id from v$sql_workar
ea_active);
SQL_TEXT
--------------------------------------------------------------------------------
select * from t order by object_name desc
select * from t order by object_id desc
另外須要說明的是臨時表空間組自己不能建立,只要把存在的臨時表空間加入按照命名規則命名的臨時表空間組中就能夠了,臨時表空間組隨之被建立,就象上面操做的那樣:
SQL> alter tablespace temp tablespace group gp1;
表空間已更改。
SQL> alter tablespace tmp tablespace group gp1;
表空間已更改。
SQL> select * from dba_tablespace_groups;
GROUP_NAME TABLESPACE_NAME
------------------------------ ------------------------------
GP1 TEMP
GP1 TMP
gp1隨着temp和tmp的加入被建立隨着temp和tmp的脫離而被刪除:
SQL> alter tablespace temp tablespace group '';
表空間已更改。
SQL> alter tablespace tmp tablespace group '';
表空間已更改。
SQL> select * from dba_tablespace_groups;
未選定行
SQL>
2007-12-28 10:26
1.jpg (13.62 KB)
2.jpg
2007-12-28 10:26
3.jpg (31.15 KB)