8288分类目录 8288分类目录 8288分类目录
  当前位置:海洋目录网 » 站长资讯 » 站长资讯 » 文章详细 订阅RssFeed

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

来源:本站原创 浏览:101次 时间:2022-01-24
Oracle Temp临时表空间及其故障处理 


Oracle 11g中Temp临时表空间、文件的新特性      

临时表空间是Oracle体系结构中比较特殊的结构。通常情境下,数据库使用者只需要设置对应的临时表空间(到用户),临时段分配等工作都是系统自动完成。当临时数据不需要时,Oracle后台进程SMON也会负责将临时段回收。

在Oracle的备份恢复体系中,临时文件的地位比较低。在进行备份动作时,RMAN都不会进行临时文件恢复。在恢复启动过程中,如果发现临时文件不存在,通常Oracle也会自动将临时文件创建出来。

 

1、Temp漫谈

 

Oracle临时表空间主要充当两个主要作用:临时表数据段分配和排序汇总溢出段。我们创建的临时表,在使用过程中,会有大量的数据段结构的分配。这个分配就是利用临时表空间。

排序汇总溢出的范围比较广泛。我们在SQL语句中进行order by/group by等操作,首先是选择PGA的内存sort area、hash area和bitmap area。如果SQL使用排序空间很高,单个server process对应的PGA不足以支撑排序要求的时候,临时表空间会充当排序段的数据写入。这样排序动作会从内存过程退化为外存储过程。

两个现象:如果我们的Temp表空间文件设置比较小,并且设置为不可自动拓展。同时我们又希望给一个很大数据表加索引,经常会遇到:create index语句长时间运行之后报错,说Temp表空间不能拓展,操作被停止。索引叶子节点是有序的,创建索引的过程也就伴随着数据库的排序动作。

另一个现象:如果我们的内存设置不合理,SQL经常包括很多“无意义”的“大排序”。这样会发现我们的Temp空间消耗比较大,一些SQL性能抖动比较明显。

合理的设置Temp空间管理策略,是应用系统架构的一个重要环节。

 

2、给临时表指定表空间

 

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操作的临时表。

 

3、临时表空间、文件的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是分配元数据。

 

4、结论

 

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、临时表空间的用途

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: 不受支持的临时表功能


了解了临时表的种类以及临时表的特性之后就可以根据需要灵活的使用临时表。

2、临时表空间的分类

上面提到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。

3、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#



4、临时表空间组

临时表空间组是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同时进行排

  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net