Oracle全局临时表和私有临时表

Oracle 全局临时表以下简称GTT(Global Temporary Table),私有临时表以下简称PTT(Private Temporary Table)。

GTT和PTT的主要区别如下:

  1. GTT更早。GTT是8i就有的特性,PTT是18c才有的特性。
  2. GTT和PTT的数据都是会话私有的,而且会话结束后数据就没有了,但GTT的定义是全局的。
  3. 事务提交时,GTT可以选择是否保留数据,PTT可以选择是否保留定义。
  4. GTT的表定义是所有会话共享的;PTT的表定义是会话私有的。

关于GTT和PTT的实验,可以分别参考Oracle Base的这篇这篇文章。

GTT和PTT建表语法示例可参考这里

GTT的实验

-- 默认是ON COMMIT DELETE ROWS
SQL> create GLOBAL TEMPORARY table gtt_test(a int);

Table created.

SQL> insert into gtt_test values(1);

1 row created.

SQL> commit;

Commit complete.

-- 因此提交后数据就没有了
SQL> select count(*) from gtt_test;

  COUNT(*)
----------
         0

-- 建立提交时保留数据的GTT
SQL> drop table gtt_test;

Table dropped.

SQL> create GLOBAL TEMPORARY table gtt_test(a int) ON COMMIT PRESERVE ROWS;

Table created.

SQL> insert into gtt_test values(1);

1 row created.

SQL> commit;

Commit complete.

SQL> select count(*) from gtt_test;

  COUNT(*)
----------
         1

-- 因为有人在使用,因此GTT无法删除。详见MOS Doc ID 800506.1
SQL> drop table gtt_test;
drop table gtt_test
           *
ERROR at line 1:
ORA-14452: attempt to create, alter or drop an index on temporary table already
in use

-- 切换会话,数据没有了
SQL> connect ssb/Welcome1@orclpdb1
Connected.

SQL> select count(*) from gtt_test;

  COUNT(*)
----------
         0

PTT的实验

PTT表名有一定的命名规则:

SQL> show parameter PRIVATE_TEMP_TABLE_PREFIX

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
private_temp_table_prefix            string      ORA$PTT_

建立默认配置(ON COMMIT DROP DEFINITION)的PTT:

SQL> create PRIVATE TEMPORARY table ora$ptt_test(a int);

Table created.

SQL> insert into ora$ptt_test values(1);

1 row created.

SQL> commit;

Commit complete.

-- 事务提交后,表定义没有了
SQL> desc ora$ptt_test;
ERROR:
ORA-04043: object ora$ptt_test does not exist

建立会话结束后删除表定义,但提交后仍保留表定义的PTT:

SQL> create PRIVATE TEMPORARY table ora$ptt_test(a int) ON COMMIT PRESERVE DEFINITION;

Table created.

SQL> insert into ora$ptt_test values(1);

1 row created.

SQL> commit;

Commit complete.

SQL> desc ora$ptt_test;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 A                                                  NUMBER(38)

SQL> connect ssb/Welcome1@orclpdb1
Connected.

SQL> desc ora$ptt_test;
ERROR:
ORA-04043: object ora$ptt_test does not exist

临时表的undo与redo

本实验参考Oracle BASE的文章:Global Temporary Tables

先来看临时表的undo:

SET AUTOTRACE ON STATISTICS;
create GLOBAL TEMPORARY table gtt_test(a int);
insert into gtt_test select 1 from dual connect by level < 10000;

9999 rows created.

Statistics
----------------------------------------------------------
         13  recursive calls
        138  db block gets
         27  consistent gets
          0  physical reads
      28528  redo size
        195  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
       9999  rows processed

SQL>
SELECT t.used_ublk,
       t.used_urec
FROM   v$transaction t,
       v$session s
WHERE  s.saddr = t.ses_addr
AND    s.audsid = SYS_CONTEXT('USERENV', 'SESSIONID');

 USED_UBLK  USED_UREC
---------- ----------
         4         55

V$TRANSACTION的说明见这里V$SESSION的帮助见这里。USED_UBLK和USED_UREC分别表示undo block和undo record的数量。

如果将临时表换为永久表,重复以上的实验,结果是一样的。

虽然 GTT 中的数据写入临时表空间,但关联的 undo 仍然写入正常的 undo 表空间,该表空间本身受redo保护,因此使用 GTT 并不会减少与保护 undo 表空间相关联的 undo 和重做。

在12c,可以将undo写到临时表空间,参看这里。这篇文章也列举了这么做的好处:

  • 写入undo表空间需要数据库以读写方式打开,所以不能在只读数据库和物理备数据库中使用全局临时表。
  • 全局临时表包含临时数据,在恢复场景中不需要这些数据,因此使用redo保护它们意味着对系统造成不必要的额外负载。
  • 与全局临时表关联的undo增加了满足undo保留期所需的总空间。

此行为由参数控制,默认不启用:

SQL> show parameter TEMP_UNDO_ENABLED

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
temp_undo_enabled                    boolean     FALSE
ALTER SESSION SET TEMP_UNDO_ENABLED = TRUE;
SET AUTOTRACE ON STATISTICS;
create GLOBAL TEMPORARY table gtt_test(a int);
insert into gtt_test select 1 from dual connect by level < 10000;

9999 rows created.


Statistics
----------------------------------------------------------
         11  recursive calls
        143  db block gets
         25  consistent gets
          0  physical reads
        280  redo size
        195  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
       9999  rows processed

SQL>
SELECT t.used_ublk,
       t.used_urec
FROM   v$transaction t,
       v$session s
WHERE  s.saddr = t.ses_addr
AND    s.audsid = SYS_CONTEXT('USERENV', 'SESSIONID');

 USED_UBLK  USED_UREC
---------- ----------
         1          1

和未开启参数前相比,undo和redo都下降了很多。

临时表与并行执行

本实验参考Oracle Blogs的文章:Parallel PL/SQL Functions and Global Temporary Tables… and Wrong Results

作者的这个例子设计得非常巧妙,我在此基础上做了修改。行数改为1000,以缩短等待时间。sleep函数也做了修改。

-- 测试表中的rpad函数可以去掉,但测试结果就有可能没那么完美
create table s as select rownum id,rpad('X',1000) pad 
from dual connect by level<=1000; 

create or replace function f_wait(id in number) return number is 
begin 
dbms_session.sleep(0.01); 
return(id); 
end; 
/

测试,无论在SQL中是否启用并行,结果都近似。从执行计划可知并行并未实际启用:

SQL> select count(*) from s where id=f_wait(id);

  COUNT(*)
----------
      1000
Elapsed: 00:00:13.78

SQL> select /*+ parallel(4) */ count(*) from s where id=f_wait(id);

  COUNT(*)
----------
      1000

Elapsed: 00:00:13.45

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     4 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |     4 |            |          |
|*  2 |   TABLE ACCESS FULL| S    |     1 |     4 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------

这是由于,PL/SQL函数要支持并行需要显式定义。

create or replace function f_wait(id in number) return number parallel_enable is 
begin 
dbms_session.sleep(0.01); 
return(id); 
end; 
/

现在,并行生效了,虽然不是完美的2.5秒:

SQL> select /*+ parallel(4) */ count(*) from s where id=f_wait(id);

  COUNT(*)
----------
      1000

Elapsed: 00:00:03.08

Execution Plan
----------------------------------------------------------
Plan hash value: 2247559131

----------------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name     | Rows  | Bytes | Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |
----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |          |     1 |     4 |    12   (0)| 00:00:01 |        |      |            |
|   1 |  SORT AGGREGATE        |          |     1 |     4 |            |          |        |      |            |
|   2 |   PX COORDINATOR       |          |       |       |            |          |        |      |            |
|   3 |    PX SEND QC (RANDOM) | :TQ10000 |     1 |     4 |            |          |  Q1,00 | P->S | QC (RAND)  |
|   4 |     SORT AGGREGATE     |          |     1 |     4 |            |          |  Q1,00 | PCWP |            |
|   5 |      PX BLOCK ITERATOR |          |     1 |     4 |    12   (0)| 00:00:01 |  Q1,00 | PCWC |            |
|*  6 |       TABLE ACCESS FULL| S        |     1 |     4 |    12   (0)| 00:00:01 |  Q1,00 | PCWP |            |
----------------------------------------------------------------------------------------------------------------

现在进入正题,来谈启用并行的 PL/SQL 函数和全局临时表。

create GLOBAL TEMPORARY table gtt_test(a int);
insert into gtt_test select 1 from dual connect by level < 10000;
select /*+ parallel(2) */ count(*) from gtt_test;

  COUNT(*)
----------
      9999

在这种情况下,我们有两个 PX 服务器扫描临时表,并且报告的计数是正确的。 这表明单个 PX 服务器能够看到用户会话之前填充的数据。 并行查询的不同之处在于,在临时表上工作的并行会话可以看到之前由 QC 填充的数据。 当查询临时表时,QC 知道临时表并将分段信息发送到 PX 服务器,以便它们可以读取数据。

查询临时表的 PL/SQL 函数改变了这种行为,此时结果就不正确了:

create table t1 (id number); 
insert into t1 values (1000); 
commit; 

create global temporary table tempstage (col1 number) on commit preserve rows; 
create or replace function f_test return number parallel_enable is 
v_var number; 
begin 
select col1 into v_var from tempstage; 
return v_var; 
end; 
/

insert into tempstage values (100); 
commit;

-- 错误的结果
SQL> select /*+ parallel(2) */ * from t1 where id>f_test;

-- 正确的结果
SQL> select * from t1 where id>f_test;

        ID
----------
      1000

它向用户返回了错误的结果。 这是因为该函数被声明为可以安全地由各个 PX 服务器执行。 每个 PX 服务器都使用自己的会话,因此它们无法看到用户会话填充的数据。 这与前面针对临时表运行查询的示例不同,在这种情况下,QC 知道涉及到临时表,这里它只看到启用了并行的函数调用。

因此,在将函数声明为启用并行时要小心,注意该函数将由 PX 服务器执行,这可能会导致一些意外行为。 考虑函数在由多个会话和进程执行时的行为方式。 仅当您确定它是安全的时才将其声明为启用并行。

参考

  • https://blogs.oracle.com/optimizer/post/global-temporary-tables-and-upgrading-to-oracle-database-12c-dont-get-caught-out
Oracle数据库中,临时表空间(temp表空间)用于存储临时数据排序操作的结果。如果临时表空间temp达到最大32GB,可以采取以下几种优化方法: 1. **调整临时表空间大小**: - 如果临时表空间大小确实需要,可以考虑增加临时表空间的大小。 - 使用`ALTER TABLESPACE`命令来增加临时表空间的大小。例如: ```sql ALTER TABLESPACE temp ADD TEMPFILE '/path/to/tempfile.dbf' SIZE 10G; ``` 2. **优化SQL查询**: - 临时表空间的使用通常与大型排序操作有关。优化SQL查询,减少不必要的排序操作,可以减少临时表空间的使用。 - 使用索引来避免全表扫描排序操作。 - 避免在SELECT语句中使用`ORDER BY`、`GROUP BY`等操作,除非确实需要。 3. **调整PGA(程序全局区)大小**: - PGA用于存储每个会话的私有数据。增加PGA的大小可以减少临时表空间的使用。 - 使用`ALTER SYSTEM`命令来调整PGA的大小。例如: ```sql ALTER SYSTEM SET pga_aggregate_target = 4G SCOPE = BOTH; ``` 4. **使用多个临时表空间**: - 创建一个或多个新的临时表空间,并将它们添加到临时表空间组中。这样可以分散临时表空间的使用压力。 - 使用`CREATE TEMPORARY TABLESPACE`命令创建新的临时表空间。例如: ```sql CREATE TEMPORARY TABLESPACE temp2 TEMPFILE '/path/to/tempfile2.dbf' SIZE 10G; ``` 5. **监控临时表空间的使用**: - 使用Oracle提供的视图工具来监控临时表空间的使用情况。 - 例如,使用`V$TEMP_SPACE_HEADER`视图来查看临时表空间的使用情况: ```sql SELECT * FROM V$TEMP_SPACE_HEADER; ``` 6. **定期维护**: - 定期进行数据库维护操作,如重建索引、清理临时表等,可以减少临时表空间的使用。 通过以上方法,可以有效优化Oracle临时表空间的使用,避免其达到最大值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值