Oracle优化神技之临时表

oracle · 浏览次数 : 0

小编点评

**临时表统计信息的缓存** **默认情况下, Oracle 12c 及之后版本的全局临时表(GTT)的统计信息是会话级别的。** 这意味着每个会话可以有自己的统计信息,这与全局共享的统计信息不同。 **如果你的数据库还是 11g 版本,那么这个统计信息的机制就是不同的,也就是SHARED。** 这意味着统计信息会从全局共享表中读取,而不会创建新的会话级别统计表。 **如果启用缓存,可以提高 GTT 查询性能。** 由于 GTT 是一个临时的表,所以访问内存比访问磁盘要快得多。缓存可以将 GTT 的数据块缓存在内存中,从而提升查询性能。 **创建索引可以提高 GTT 的查询性能。** 索引可以创建在 GTT 上,并与 GTT 中的原始数据表相关联。 **临时表是否设置了 CACHE 属性可以影响其查询性能。** CACHE 属性可以指定 GTT 的缓存属性,例如缓存时间、缓存大小等。如果缓存属性设置正确,可以提高 GTT 的查询性能。

正文

Oracle临时表在处理临时数据、会话数据隔离和复杂查询优化方面非常有用。

其底层逻辑是通过Oracle特殊的临时表来减少I/O操作和日志开销,提高了数据库性能和查询效率。开发者可以根据具体需求和场景,合理使用临时表来简化数据处理逻辑和提高系统性能。

早期开发人员在使用Oracle数据库时,经常因为不熟悉或不了解全局临时表(Global Temporary Table,下文简称GTT)的特性,因而自行定义了所谓的“临时表”,不但增加了开发复杂度,比如需要自行做数据清理和会话隔离等问题,还因高频操作这类表产生了大量重做日志(redo logs),进而增加了I/O负载和系统开销,主要代价这么多,最终的应用性能还不够好。

所幸这类问题随着用户量的提升,大家口口相传这个最佳实践,后续开发已经很少会犯这类低级问题。

那是不是用了Oracle的临时表就可以高枕无忧了呢?

最近笔者在某客户遇到一个临时表的问题,在分析这个客户问题的过程,也和大家一起来回顾下有关Oracle临时表的知识。

  • 1.创建临时表
  • 2.临时表统计信息
  • 3.临时表索引
  • 4.临时表是否cache
  • 5.临时表相关问题

1.创建临时表

本次遇到问题的临时表,是使用的Oracle的GTT,且定义表中数据是基于session-specific的类型,脱敏后的创建语句为:

CREATE GLOBAL TEMPORARY TABLE "JINGYU"."G_T_T1"
("ID" NUMBER(10,0) NOT NULL ENABLE,
"NAME" VARCHAR2(30) NOT NULL ENABLE)
ON COMMIT PRESERVE ROWS;

下面是官方文档截图,比较了GTT和PTT的差异:


除了上面提到的命名规则等差异之外,还要补充一点:
GTT是8i后就已经支持的技术,而PTT要在18c及以后版本才支持。

关于GTT的两种类型,文档说明如下:


根据你的应用需求选择,简单说就是如果想在事务结束就清空表,选择DELETE ROWS类型;如果想在会话结束才清空表,就选择PRESERVE ROWS类型。

2.临时表统计信息

临时表也是有统计信息的,而且临时表统计信息的机制在不同版本也有差异。
先看下在19c版本下表现:

我先在session1中插入两条测试数据,收集统计信息:

INSERT INTO G_T_T1 VALUES (1, 'Alfred');
INSERT INTO G_T_T1 VALUES (2, 'Mcdull');

BEGIN
    DBMS_STATS.GATHER_TABLE_STATS(
        ownname => 'JINGYU',
        tabname => 'G_T_T1',
        cascade => TRUE
    );
END;
/

SELECT owner, table_name, num_rows, blocks, empty_blocks, avg_space
FROM dba_tab_statistics
WHERE table_name = 'G_T_T1';

紧接着在session2中插入一条数据,收集统计信息:

INSERT INTO G_T_T1 VALUES (3, 'Test');

BEGIN
    DBMS_STATS.GATHER_TABLE_STATS(
        ownname => 'JINGYU',
        tabname => 'G_T_T1',
        cascade => TRUE
    );
END;
/

SELECT owner, table_name, num_rows, blocks, empty_blocks, avg_space
FROM dba_tab_statistics
WHERE table_name = 'G_T_T1';

两个查询结果是不一样的,两行结果,分别显示为2条和1条数据的统计信息。
--result1:

08:52:42 PRIMARY @DB0913_9DF_IAD -> JINGYU @DEMO1> SELECT owner, table_name, num_rows, blocks, empty_blocks, avg_space
FROM dba_tab_statistics
WHERE table_name = 'G_T_T1';08:53:39   2  08:53:39   3

OWNER			       TABLE_NAME			NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
------------------------------ ------------------------------ ---------- ---------- ------------ ----------
JINGYU			       G_T_T1
JINGYU			       G_T_T1				       2	  1	       0	  0

Elapsed: 00:00:00.02

--result2:

08:53:35 PRIMARY @DB0913_9DF_IAD -> JINGYU @DEMO1> SELECT owner, table_name, num_rows, blocks, empty_blocks, avg_space
FROM dba_tab_statistics
WHERE table_name = 'G_T_T1';08:53:44   2  08:53:44   3

OWNER			       TABLE_NAME			NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
------------------------------ ------------------------------ ---------- ---------- ------------ ----------
JINGYU			       G_T_T1
JINGYU			       G_T_T1				       1	  1	       0	  0

Elapsed: 00:00:00.01

这说明全局临时表在19c版本的默认统计信息是session级别。

--查看全局临时表的统计信息首选项设置
SELECT DBMS_STATS.GET_PREFS('GLOBAL_TEMP_TABLE_STATS', 'JINGYU', 'G_T_T1') AS global_temp_table_stats
FROM dual;

09:04:59 PRIMARY @DB0913_9DF_IAD -> JINGYU @DEMO1> --查看全局临时表的统计信息首选项设置
SELECT DBMS_STATS.GET_PREFS('GLOBAL_TEMP_TABLE_STATS', 'JINGYU', 'G_T_T1') AS global_temp_table_stats
FROM dual;
09:05:00 PRIMARY @DB0913_9DF_IAD -> JINGYU @DEMO1> 09:05:00   2
GLOBAL_TEMP_TABLE_STATS
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SESSION

Elapsed: 00:00:00.01

具体查了下文档,说是在Oracle 12c及之后的版本中,全局临时表(GTT)的统计信息确实有可能是会话级别的。这意味着每个会话可以有自己的统计信息,这与全局共享的统计信息不同。

Default in 12c is now SESSION global temporary table statistics.  Consider whether your database application depends on SHARED global temporary table statistics. 

而如果你的数据库还是11g版本,那么这个统计信息的机制就是不同的,也就是SHARED,这里模拟将19c的这个也修改为SHARED模式,看看表现:

BEGIN
   DBMS_STATS.SET_TABLE_PREFS(
      ownname => 'JINGYU',
      tabname => 'G_T_T1',
      pname => 'GLOBAL_TEMP_TABLE_STATS',
      pvalue => 'SHARED'
   );
END;
/

BEGIN
   DBMS_STATS.GATHER_TABLE_STATS(
      ownname => 'JINGYU',
      tabname => 'G_T_T1',
      cascade => TRUE
   );
END;
/

成功修改为shared后,当表中有3条数据时收集统计信息后再次查询,会发现这个统计信息在其他会话也可以访问到:

09:10:46 PRIMARY @DB0913_9DF_IAD -> JINGYU @DEMO1> r
  1  SELECT owner, table_name, num_rows, blocks, empty_blocks, avg_space
  2  FROM dba_tab_statistics
  3* WHERE table_name = 'G_T_T1'

OWNER			       TABLE_NAME			NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
------------------------------ ------------------------------ ---------- ---------- ------------ ----------
JINGYU			       G_T_T1				       3	  1	       0	  0

Elapsed: 00:00:00.02
09:10:48 PRIMARY @DB0913_9DF_IAD -> JINGYU @DEMO1>

3.临时表索引

GTT是可以创建索引的,当然这个索引也是临时的属性,另外还可以在GTT上创建视图和触发器。

You can create indexes for global (not private) temporary tables with the CREATE INDEX
statement. These indexes are also temporary. The data in the index has the same
session or transaction scope as the data in the temporary table. You can also create a
view or trigger on a global temporary table.

4.临时表是否cache

使用ALTER TABLE ... CACHE可以提高GTT查询性能。

ALTER TABLE G_T_T1 CACHE;

在Oracle中,通过使用ALTER TABLE ... CACHE语句可以在创建GTT时指定缓存属性。GTT是一种特殊类型的数据库表,用于存储临时数据,数据在会话结束或事务完成时被清除。CACHE关键字在这里的作用是指定GTT的缓存属性。

具体来说,CACHE关键字指示Oracle数据库将GTT的数据块缓存在内存中,而不是直接写入磁盘。这样做的好处是可以提高查询临时表数据的性能,因为访问内存通常比访问磁盘要快得多。

需要注意的是,使用CACHE会占用更多的内存空间,因为临时表的数据在会话结束或事务完成后会被清除,所以对于大型数据量或长时间运行的会话,可能需要权衡内存利用和性能。

临时表是否设置了CACHE属性是可以通过dbms_metadata.get_ddl中看到cache标识的:

select dbms_metadata.get_ddl('TABLE','G_T_T1','JINGYU') from dual;


  CREATE GLOBAL TEMPORARY TABLE "JINGYU"."G_T_T1"
   (	"ID" NUMBER(10,0) NOT NULL ENABLE,
	"NAME" VARCHAR2(30) NOT NULL ENABLE
   ) ON COMMIT PRESERVE ROWS
   CACHE

特别需要注意的是,这个简单的cache命令,在线直接执行很可能会失败:

alter table G_T_T1 cache;

会报错ORA-14450:

14:46:14 PRIMARY @DB0913_9DF_IAD -> JINGYU @DEMO1> alter table G_T_T1 cache;
alter table G_T_T1 cache
*
ERROR at line 1:
ORA-14450: attempt to access a transactional temp table already in use


Elapsed: 00:00:00.02

这是因为有会话在使用GTT,确保没有使用的会话重新执行才能成功:

14:51:14 PRIMARY @DB0913_9DF_IAD -> JINGYU @DEMO1> alter table G_T_T1 cache;

Table altered.

Elapsed: 00:00:00.10

5.临时表相关问题

顺便查了一些相关问题,作为扩展供大家学习参考:

FYI:

与Oracle优化神技之临时表相似的内容:

Oracle优化神技之临时表

Oracle临时表在处理临时数据、会话数据隔离和复杂查询优化方面非常有用。 其底层逻辑是通过Oracle特殊的临时表来减少I/O操作和日志开销,提高了数据库性能和查询效率。开发者可以根据具体需求和场景,合理使用临时表来简化数据处理逻辑和提高系统性能。 早期开发人员在使用Oracle数据库时,经常因为

[转帖]Oracle优化案例:vfs_cache_pressure和min_free_kbytes解决RMAN挂起问题

https://www.modb.pro/db/34028 环境: Oracle 11gr2 + dataguard 512GB内存 + 128核cpu + 高性能存储服务器 uname -an Linux dbhost 2.6.18-238.el5 #1 SMP Sun Dec 19 14:22:

[转帖]Oracle 性能优化 之 游标及 SQL

https://www.cnblogs.com/augus007/articles/9273236.html 一、游标 我们要先说一下游标这个概念。 从 Oracle 数据库管理员的角度上说,游标是对存储在库缓存中的可执行对象的统称。SQL 语句是存储在库缓存中的,它是游标。除了它之外,还有 Ora

[转帖]优化Oracle数据库的参数设置原创

https://vip.kingdee.com/article/372401660284519936?productLineId=8 1.登录数据库,创建参数文件备份 2.修改参数 数据库参数修改存在风险,仅供参考 1.登录数据库,创建参数文件备份 sqlplus / as sysdba 用SYSD

[转帖]Oracle JDBC中的语句缓存

老熊 Oracle性能优化 2013-09-13 在Oracle数据库中,SQL解析有几种: 硬解析,过多的硬解析在系统中产生shared pool latch和library cache liatch争用,消耗过多的shared pool,使得系统不具有可伸缩性。 软解析,过多的软解析仍然可能会导

Oracle "脑残" CBO 优化案例

今天晚上下班回来才有空看群,群友发了一条很简单的慢SQL问怎么优化。 非常简单,我自己模拟的数据。 表结构: -- auto-generated definition CREATE TABLE HHHHHH ( ID NUMBER NOT NULL PRIMARY KEY, NAME VARCHAR

优化利器In-Memory开启和效果

本文主要介绍Oracle In-Memory 选件,Oracle在12.1.0.2就已经推出了In-Memory这个选件,现在通常会建议所有使用19.8及之后版本的用户,有条件都要留给In-memory一点内存区域。 因为该选件在19.8之后推出了16GB及以下免费使用的福利,作为优化的又一利器。

[转帖]内存优化(开启内存大页vm.nr_hugepages)

大页内存(hugepages) 为优化内存管理引入了hugepages 可以自定义设置、将原来标准内存也4k设置为更大。 hugepages 优点: 使得Oracle SGA 不可交换; 减轻 TLB 的压力; 减少页表的开销; 减少页表查询的开销; 提升内存访问的整体性能; oracle建议设置h

Oracle CloudWorld 2022 - 使用Oracle MAA实现应用程序的连续可用性

每每谈到Oracle MAA,大家条件反射般就会想到Oracle的RAC和ADG等核心选件,当然,这些技术有口皆碑,也的确是MAA的构建基础,但本文我们不再过多谈这些耳熟能详的技术,而是来跟大家探讨下在此基础之上,我们如何将基础MAA优化到业务连续性MAA这个程度,最终实现应用程序的连续可用性。 在

小知识:IN和EXISTS的用法及效率验证

环境: Oracle 19.16 多租户架构 经常会在网上看到有人写exists和in的效率区别,其实在新版本的数据库中,是不存在这个问题的,优化器会自己判断选择最优的执行计划。 为了直观的说明,我在PDB中构造如下测试用例: vi 1.sql select count(*) from v$acti