Oracle Temp临时表空间及其故障处理

 Oracle Temp临时表空间及其故障处理 


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   ?      

  1. select * from   
  2. (select username,session_addr,sql_id,contents,segtype,blocks*8/1024/1024 gb   
  3. from v$sort_usage order by blocks desc)   
  4. where rownum<=200;  
  5.   
  6. USERNAME    SESSION_ADDR     SQL_ID        CONTENTS  SEGTYPE            GB  
  7. ----------  ---------------- ------------- --------- --------- -----------  
  8. XXXX        0700002949BCD8A0 291nk7db4bwdh TEMPORARY SORT      .9677734375  
  9. XXXX        070000294BD99628 291nk7db4bwdh TEMPORARY SORT      .9677734375  
  10. XXXX        070000294CD10480 291nk7db4bwdh TEMPORARY SORT      .9677734375  
  11. XXXX        070000294DD1AC88 291nk7db4bwdh TEMPORARY SORT      .9677734375  
  12. XXXX        070000294CD68D70 291nk7db4bwdh TEMPORARY SORT      .9677734375  
  13. XXXX        070000294DBDF760 291nk7db4bwdh TEMPORARY SORT      .9677734375  
  14. XXXX        070000294EDB5D10 291nk7db4bwdh TEMPORARY SORT      .9677734375  
  15. XXXX        070000294FD7D818 291nk7db4bwdh TEMPORARY SORT      .9677734375  
  16. ...结果较多,忽略部分输出...  

SQL_ID都是同样的,那这个SQL是否有其特殊性呢?SEGTYPE为SORT代表这个临时段是“排序段”,用于SQL排序,大小竟然也是同样,会话占用的临时段大小将近1GB,几百个会话加在一块儿,想不让临时表空间不撑大都难。

看看这个相同的SQL ID表明的SQL是什么:

view plain   copy to clipboard   print   ?      

  1. SQL> @sqlbyid 291nk7db4bwdh  
  2.   
  3. SQL_FULLTEXT  
  4. --------------------------------------------------------------------------------------------------------------  
  5.  SELECT  A.LLEVEL,  A.LMODE  FROM TABLE_XXX A  WHERE A.SERVICE_NAME = :SERVICE_NAME AND STATE='Y'  

很明显,这是一条很是简单的SQL,没有ORDER BY ,也没有GROUP BY、UNION、DISTINCT等须要排序的,TABLE_XXX是一张普通的表,而不是视图。出现了什么问题?会不会是v$sort_usage的SQL_ID列有错误?咱们查看其中一个会话正在执行的SQL:

view plain   copy to clipboard   print   ?      

  1. select sid,prev_sql_id, sql_id from v$session where saddr='070000294AC0D050';  
  2.   
  3.         SID PREV_SQL_ID   SQL_ID  
  4. ----------- ------------- -------------  
  5.        3163 291nk7db4bwdh  

v$sort_usage中看到某个会话当前没有执行任何SQL,v$sort_usage中的SQL_ID是该会话前一条执行的SQL。为何这里显示的是会话前一条执行的SQL,关于这个问题后面再详述,但至少有一点是能够判断的:若是大量的临时段都是由会话当前正在执行的SQL所产生的,那说明同时有几百个会话在执行须要大量临时空间的SQL,那系统早就崩溃了。因此这些临时表空间的占用不该该是由当前在执行的SQL所产生的,至少大部分不是。

大部分人的一个错误观点是,临时表空间中当前占用的空间是由会话当前正在执行的SQL所产生的。上面的一个简单的分析判断,状况不该该是这样。咱们能够基于查询类SQL的执行过程来分析:

  1. 解析SQL语句(Parse),生成一个游标(Open Cursor)。
  2. 执行SQL语句(Execute),严格说就是执行新产生的游标。
  3. 在游标中取数据(Fetch)。
  4. 关闭游标(Close Cursor)。

关键在第3步。你们都知道取数据有一个array size的概念,表示一次从游标中取多少条数据,这是一个循环的过程。若是SQL查询获得的数据有1000条,每次取100条,则须要取10次。对于Fetch Cursor,有两点:

  1. 一个游标,或者说一条SQL语句,并不要求客户端把全部数据取完,只取了一部分数据就关闭游标也是能够的。
  2. 只要尚未关闭游标,数据库就要维护该游标的状态,若是是排序的SQL,也须要维持该SQL已经排好序的数据。

很显然,从上述第2点能够知道,若是一条SQL使用了临时段来排序,在SQL对应的游标没关闭的状况下,Oracle数据库不会去释放临时段,由于对于Oracle数据库来讲,它不会知道客户端是否还要继续取游标的数据。

基于这样的分析,咱们只须要随便选择一个占用了接近1GB的会话,查询v$open_cursor,查看其打开的游标中是否有大数据量排序的SQL:

view plain   copy to clipboard   print   ?      

  1. SQL> select sql_id,sorts,rows_processed/executions from v$sql  
  2.   2  where parsing_schema_name='ACCT' and executions>0 and sorts>0  
  3.   3  and sql_id in (select sql_id from v$open_cursor where sid=4505)  
  4.   4  order by 3;  
  5.     
  6.   SQL_ID              SORTS ROWS_PROCESSED/EXECUTIONS  
  7. ------------- ----------- -------------------------  
  8. ...省略部分输出结果...  
  9. 86vp997jbz7s6       63283                       593  
  10. cfpdpb526ad43         592               35859.79899  
  11. cfpdpb526ad43         188               55893.61702  
  12. cfpdpb526ad43         443                     71000  

最后三个游标,实际上都是同一条SQL语句,排序的数据量最大,咱们来看看这条SQL是什么:

view plain   copy to clipboard   print   ?      

  1. @sqlbyid cfpdpb526ad43  
  2.   
  3. SQL_FULLTEXT  
  4. ---------------------------------------------------------------------------------------------------  
  5. select ... from  c, b, a, d, e where ... order by d.billing_cycle_id desc,e.offer_name,a.acc_name  

基于为客户保密的缘由,SQL作了处理,能知道这条SQL的确是排了序就行,不过在SQL中看不出来的是,这条SQL没有任何实质性的可以过滤大量数据的条件。那么咱们count(*)这条SQL语句看看:

view plain   copy to clipboard   print   ?      

  1. COUNT(*)  
  2. --------  
  3. 12122698  

出来的结果竟然有1200多万条数据,一个前台应用,不知道取1200多万条数据干吗。可是从rows_processed/executions只有几万的结果来看,应用在取了几万条数据以后,因为某些缘由(最大的可能就是不能再处理更多的数据),再也不继续取数据,可是游标也一直没有关闭。

比较容易就能进行演示sort by时临时表空间的占用。

view plain   copy to clipboard   print   ?      

  1. 根据dba_objects建一个测试表T1,使其数据量达到2000万行。  
  2.  select count(*) from t1;  
  3.   
  4.    COUNT(*)  
  5. -----------  
  6.    20171200  
  7.    
  8. 而后将SQL工做区设置为手动模式,设置sort内存大小限制为200M:  
  9.  alter session set workarea_size_policy=manual;  
  10.  alter session set sort_area_size=209715200;  
  11.   
  12. 查询获得当前的会话sid:  
  13.  select sid from v$mystat where rownum< =1;  
  14.   
  15.         SID  
  16. -----------  
  17.        2111  
  18.   
  19. 执行这下面的代码:  
  20.  declare  
  21.   2     v_object_name varchar2(100);  
  22.   3     v_dummy varchar2(100);  
  23.   4  begin  
  24.   5    for rec in (select * from t1 order by object_id,object_name) loop  
  25.   6       select object_type into v_dummy from t1 where rownum<=1;  
  26.   7       select object_name into v_object_name from dba_objects where object_id=rec.object_id;  
  27.   8       dbms_lock.sleep(60*10);  
  28.   9       exit;  
  29.  10    end loop;  
  30.  11  end;  
  31.  12  /  
  32. 这段代码会打开一个游标,对2000万的数据量进行排序,而后在循环中只取一条数据,而后就进入sleep。在另外一个窗口中监控到2111这个会话的event变成了PL/SQL lock timer,就去查询v$sort_usage:  
  33. select a.sql_id sort_sql_id,b.sql_id,b.prev_sql_id, contents,segtype,blocks*8/1024/1024 gb   
  34.   2  from v$sort_usage a,v$session b   
  35.   3  where a.session_addr=b.saddr  
  36.   4  and b.sid=2111;  
  37.   
  38. SORT_SQL_ID   SQL_ID        PREV_SQL_ID   CONTENTS  SEGTYPE            GB  
  39. ------------- ------------- ------------- --------- --------- -----------  
  40. fabh24prgk2sj bhzf316mdc07w fabh24prgk2sj TEMPORARY SORT      1.444824219  
  41. 能够看到v$sort_usage中的SQL_ID(即上述结果中SORT_SQL_ID)与v$session中的pre_sql_id一致,这条SQL是:  
  42.   
  43. @sqlbyid fabh24prgk2sj  
  44. SQL_FULLTEXT  
  45. --------------------------------------------------------  
  46. SELECT OBJECT_NAME FROM DBA_OBJECTS WHERE OBJECT_ID=:B1  
  47.   
  48. 而实际上当前正在执行的SQL是:  
  49.  @sqlbyid bhzf316mdc07w  
  50.   
  51. SQL_FULLTEXT  
  52. ---------------------------------------------------------------------------  
  53. declare  
  54.    v_object_name varchar2(100);  
  55.    v_dummy varchar2(100);  
  56. begin  
  57.   for rec in (select * from t1 order by object_id,object_name) loop  
  58.      select object_type into v_dummy from t1 where rownum<=1;  
  59.      select object_name into v_object_name from dba_objects where object_id=rec.object_id;  
  60.      dbms_lock.sleep(60*10);  
  61.      exit;  
  62.   end loop;  
  63. end;  

问题分析到这里,很明显确认的是,应用存在问题,也许是业务逻辑问题;也许是根据前台选择的条件拼接的SQL,可是没有任何条件时就查询了全部数据。接下来就是找来开发人员,至于后面的事就跟这个主题没有太大关系。咱们能够根据这个案例来进一步展开,去探寻临时表空间的更多知识点。

这里要展开的第1点是,v$sort_usage中的sql_id是否是会话正在执行的SQL,咱们去看看视图fixed_View_definition就知道了:

view plain   copy to clipboard   print   ?      

  1. select x$ktsso.inst_id, username, username, ktssoses, ktssosno, prev_sql_addr, prev_hash_value,  
  2. prev_sql_id, ktssotsn, decode(ktssocnt, 0, 'PERMANENT', 1, 'TEMPORARY'), decode(ktssosegt, 1,  
  3. 'SORT', 2, 'HASH', 3, 'DATA', 4, 'INDEX', 5, 'LOB_DATA', 6, 'LOB_INDEX' , 'UNDEFINED'), ktssofno,  
  4. ktssobno, ktssoexts, ktssoblks, ktssorfno from x$ktsso, v$session where ktssoses = v$session.saddr  
  5. and ktssosno = v$session.serial#  

原来在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   ?      

  1. select ktssosqlid from x$ktsso, v$session where ktssoses = v$session.saddr  
  2.   2  and ktssosno = v$session.serial#  
  3.   3  and v$session.sid=2111;  
  4.   
  5. KTSSOSQLID  
  6. -------------  
  7. 60t6fmjsw6v8y  
  8.   
  9. @sqlbyid 60t6fmjsw6v8y  
  10.   
  11. SQL_FULLTEXT  
  12. ---------------------------------------------------------------------------  
  13. SELECT * FROM T1 ORDER BY OBJECT_ID,OBJECT_NAME  

能够看到的是咱们查询到了真正产生临时段的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   ?      

  1. select k.inst_id "INST_ID",  
  2.        ktssoses "SADDR",  
  3.        sid,  
  4.        ktssosno "SERIAL#",  
  5.        username "USERNAME",  
  6.        osuser "OSUSER",   
  7.        ktssosqlid "SQL_ID",  
  8.        ktssotsn "TABLESPACE",  
  9.        decode(ktssocnt, 0, 'PERMANENT', 1, 'TEMPORARY') "CONTENTS",  
  10.        --注意在12c的v$sort_usage定义中TABLESPACE和CONTENTS已经发生变化了。  
  11.        decode(ktssosegt, 1, 'SORT', 2, 'HASH', 3, 'DATA', 4, 'INDEX',   
  12.           5, 'LOB_DATA', 6, 'LOB_INDEX' , 'UNDEFINED') "SEGTYPE",  
  13.        ktssofno "SEGFILE#",  
  14.        ktssobno "SEGBLK#",  
  15.        ktssoexts "EXTENTS",  
  16.        ktssoblks "BLOCKS",  
  17.        round(ktssoblks*p.value/1024/1024, 2) "SIZE_MB",  
  18.        ktssorfno "SEGRFNO#"  
  19. from x$ktsso k, v$session s,   
  20.      (select value from v$parameter where name='db_block_size') p   
  21. where ktssoses = s.saddr  
  22.   and ktssosno = s.serial#;  

要展开的第2点是,v$sort_usage中的SEGTYPE列的不一样的值各有什么意义:

  1. SORT:SQL排序使用的临时段,包括order by、group by、union、distinct、窗口函数(window function)、建索引等产生的排序。
  2. DATA:临时表(Global Temporary Table)存储数据使有的段。
  3. INDEX:临时表上建的索引使用的段。
  4. HASH:hash算法,如hash链接所使用的临时段。
  5. LOB_DATA和LOB_INDEX:临时LOB使用的临时段。

根据上述的段类型,大致能够分为三类占用:

  1. SQL语句排序、HASH JOIN占用
  2. 临时表占用
  3. 临时LOB对象占用

临时表空间的异常占用,一种缓步增加的,另外一种状况:一下撑满的一般是一个极大数据量的排序或极大的索引的建立。缓步增加的状况,跟系统的内存被逐渐占用相似,存在“泄露”。好比排序的SQL游标没有关闭,好比本文的案例;好比会话级临时表产生了数据后一直没有清除;临时LOB对象没有清理或泄露。前两种比较好去分析处理,可是临时LOB的泄露问题就复杂不少。

来看一个测试:

view plain   copy to clipboard   print   ?      

  1.  select sid from v$mystat where rownum<=1;  
  2.   
  3.         SID  
  4. -----------  
  5.        1773  
  6.  declare  
  7.   2    v_lob clob;  
  8.   3  begin  
  9.   4    dbms_lob.createtemporary(v_lob,true);  
  10.   5    dbms_lob.writeappend(v_lob,1000,lpad('a',1000,'a'));  
  11.   6  end;  
  12.   7  /  

上述的代码执行完以后,在另外一个窗口中,咱们查询v$sort_usage:

view plain   copy to clipboard   print   ?      

  1. select a.sql_id sort_sql_id,b.sql_id,b.prev_sql_id, contents,segtype,blocks*8/1024/1024 gb   
  2.   2  from v$sort_usage a,v$session b   
  3.   3  where a.session_addr=b.saddr  
  4.   4  and b.sid=1773;  
  5.   
  6. SORT_SQL_ID   SQL_ID        PREV_SQL_ID   CONTENTS  SEGTYPE            GB  
  7. ------------- ------------- ------------- --------- --------- -----------  
  8. 9babjv8yq8ru3               9babjv8yq8ru3 TEMPORARY LOB_DATA  .0004882813  
  9.   
  10. @sqlbyid 9babjv8yq8ru3  
  11.   
  12. SQL_FULLTEXT  
  13. ---------------------------------------------------------------------------  
  14. BEGIN DBMS_OUTPUT.GET_LINES(:LINES, :NUMLINES); END;  

能够看到,这个会话已经产生了类型为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)  
  
   

相关文章
相关标签/搜索