Oracle 性能优化:日志暴增、Undo 告急?全局临时表来救火!

来源:这里教程网 时间:2026-03-03 23:03:23 作者:

作为 DBA,我们最怕听到客户抱怨:“数据库怎么又慢了?”、“归档日志怎么又满了?”。

最近我们就遇到了这样一个案例:客户生产系统的归档日志量突然暴增,Undo 表空间的使用率也居高不下,报警短信响个不停。

经过紧急排查,我们发现罪魁祸首竟然是一个不起眼的“中间表”。今天,我们就来聊聊这个案例,以及如何利用 Oracle 的全局临时表特性,优雅地解决这类问题。

案发经过:一张小表引发的“血案”

接到报警后,我们迅速查看了 AWR 报告和相关视图。诊断发现,系统中有大量的 DELETE 语句在一小时内被执行了几十万次!

DELETE FROM inter_data_table WHERE ...

和开发团队沟通后得知,这张 inter_data_table 是一张中间表。业务程序的逻辑是:

先把复杂的计算结果插入这张表。

后续步骤读取这张表的数据进行处理。

处理完后,执行 DELETE 把刚才的数据删掉,以便下次使用。

问题就出在这个“用完即删”上!

在 Oracle 中,DELETE 是一个非常昂贵的操作。它不仅会产生大量的 Undo 数据(用于回滚),还会产生大量的 Redo 日志(用于恢复)。这种高频的“插入-删除”模式,直接导致了日志暴增和 Undo 紧张。

解决方案:全局临时表 (GTT)

其实,这种“用完即焚”的数据,天生就是给全局临时表准备的。

全局临时表的数据只在当前会话或当前事务中有效。一旦会话断开或事务提交,数据就会自动消失。这就意味着:

无需手动 DELETE:省去了昂贵的删除操作。

极少的日志量:临时表的 DML 操作产生的 Redo 和 Undo 都远少于普通表。

数据隔离:不同会话之间的数据互不可见,不用担心并发冲突。

实战演示:两种类型的临时表

Oracle 的全局临时表分为两种:基于会话 (Session) 和 基于事务 (Transaction)。

1. 基于会话的临时表 (ON COMMIT PRESERVE ROWS)

这种表的数据一直保留,直到你断开连接 (Exit Session) 或者手动清空。

-- 创建语法

CREATE GLOBAL TEMPORARY TABLE zmh_session ON COMMIT PRESERVE ROWS AS SELECT * FROM dba_objects WHERE 1=2;

-- 查看属性

SELECT table_name, temporary, duration FROM user_tables WHERE table_name='ZMH_SESSION';

-- 结果:SYS$SESSION (表示基于会话)

2. 基于事务的临时表 (ON COMMIT DELETE ROWS)

这种表的数据只在当前事务有效。一旦你执行了 COMMIT,数据就自动清空了。

-- 创建语法

CREATE GLOBAL TEMPORARY TABLE zmh_transaction ON COMMIT DELETE ROWS AS SELECT * FROM dba_objects WHERE 1=2;

-- 查看属性

SELECT table_name, temporary, duration FROM user_tables WHERE table_name='ZMH_TRANSACTION';

-- 结果:SYS$TRANSACTION (表示基于事务)

效果验证:数据去哪了?

我们来做一个简单的实验,看看这两种表在 COMMIT 前后的表现。

Step 1: 插入数据

INSERT INTO zmh_session SELECT * FROM dba_objects;INSERT INTO zmh_transaction SELECT * FROM dba_objects;

-- 此时查询,两张表都有数据

SELECT count(*) FROM zmh_session;      -- 72496SELECT count(*) FROM zmh_transaction;  -- 72496

Step 2: 提交事务 (COMMIT)

COMMIT;

-- 再次查询

SELECT count(*) FROM zmh_session;      -- 72496 (数据还在,因为是基于会话的)SELECT count(*) FROM zmh_transaction;  -- 0     (数据没了!COMMIT 后自动清空)

Step 3: 断开重连

退出当前 SQLPlus 会话,重新登录。

-- 再次查询

SELECT count(*) FROM zmh_session;      -- 0     (会话断开后,数据自动清空)

总结

回到开头的案例,我们建议开发团队将那张普通的中间表改造成基于事务的全局临时表 (ON COMMIT DELETE ROWS)。

改造后的效果立竿见影:

程序里的 DELETE 语句直接删掉,代码更简洁。

归档日志量骤降,磁盘空间不再告急。

Undo 表空间压力释放,系统整体性能大幅提升。

下次再遇到这种存临时数据的需求,千万别再用普通表 + DELETE 了,全局临时表才是你的最佳拍档!

相关推荐