ITPub博客

首页 > 数据库 > Oracle > Resumable Statement的用法

Resumable Statement的用法

原创 Oracle 作者:a960549548 时间:2026-09-02 15:17:25 0 删除 编辑

想象一下下面的场景:

一个跑了四、五个小时的批量 INSERT ,眼看到了最后一步,屏幕上突然蹦出:

ORA-01653: unable to extend table TEST.T_RESUMABLE by 128 in tablespace SMALL_TBS

空间不够,语句报错,前面几个小时的劳动全部回滚,只能加好空间从头再来。如果把批量作业安排在夜里,第二天早上迎接你的就是这次彻底的失败。

 

其实 Oracle 早在 9i 时代就为这类场景准备了一个非常冷门但极其贴心的特性 —— 可恢复语句( Resumable Statement ),以及配套的 DBMS_RESUMABLE 包:遇到空间类错误时,语句不是立即报错,而是挂起等待; DBA 趁机把空间补上,语句自动从断点继续执行,就像什么都没发生过。

  工作原理是 开启可恢复模式后,会话中的语句在遇到空间类错误时不会直接失败,而是进入挂起状态:

  语句所在的事务不回滚,行锁、 undo 等资源继续持有;

数据库在告警日志中记录挂起事件,DBA_RESUMABLE 视图中可以看到挂起的会话、原因和错误信息;

挂起的会话在 V$SESSION_WAIT 中表现为等待事件 statement suspended, wait error to be cleared ,所以从监控上看它就像 " 卡住 " 了一样;

一旦空间问题被解决(扩数据文件、加数据文件、扩配额),语句自动恢复执行,无需任何人工干预;

如果一直到超时时间(默认 7200 秒,即 2 小时)问题都没有解决,语句才会真正报出原始错误并回滚。

可以触发挂起的三类错误条件:

空间耗尽(表、索引、临时段、undo 等) ORA-01653 (表无法扩展)、 ORA-01654 (索引无法扩展)

达到最大扩展数 ORA-01631 、 ORA-01632

配额超限 ORA-01536: space quota exceeded for tablespace

注意:只有空间类错误会挂起。锁等待、ORA-01555 之类的错误照常抛出,这个特性管不了。 SELECT 语句(比如大排序把临时表空间耗尽)同样受益于该特性。

 

如何开启

前提:用户必须拥有 RESUMABLE 系统权限(包含在 DBA 角色中),因为挂起的语句会一直持有系统资源。

  方式一:会话级开启(最常用)

 SQL> alter session enable resumable timeout 600 name 'fill_small_tbs';
 Session altered.
TIMEOUT 不写时默认 7200 秒;
NAME 用于在 DBA_RESUMABLE 中标识这个挂起的作业,方便定位。

方式二:实例级开启

  10g 起提供了 RESUMABLE_TIMEOUT 初始化参数(默认 0 ,即关闭),可以直接设置,也可以动态修改:

SQL> alter system set resumable_timeout = 3600;
 
System altered.


方式三:给特定用户默认开启

  通过 LOGON 触发器对该用户的会话自动执行 ALTER SESSION ENABLE RESUMABLE ,适合 " 某个账号专门跑批 " 的场景。

 

两点提醒:开启动作只对之后开始的语句生效,救不了正在报错的语句;挂起期间锁和 undo 都不释放,所以不要在 OLTP 高并发会话上无差别开启 —— 它是为批量作业、索引重建、大数据导入这类场景准备的。

 

DBMS_RESUMABLE 包结构

SQL> desc dbms_resumable
PROCEDURE ABORT
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SESSION_ID                     NUMBER                  IN
FUNCTION GET_SESSION_TIMEOUT RETURNS NUMBER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SESSION_ID                     NUMBER                  IN
PROCEDURE SET_SESSION_TIMEOUT
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SESSION_ID                     NUMBER                  IN
 TIMEOUT                        NUMBER                  IN
PROCEDURE SET_TIMEOUT
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 TIMEOUT                        NUMBER                  IN
FUNCTION SPACE_ERROR_INFO RETURNS BOOLEAN
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 ERROR_TYPE                     VARCHAR2                OUT
 OBJECT_TYPE                    VARCHAR2                OUT
 OBJECT_OWNER                   VARCHAR2                OUT
 TABLE_SPACE_NAME               VARCHAR2                OUT
 OBJECT_NAME                    VARCHAR2                OUT
 SUB_OBJECT_NAME                VARCHAR2                OUT


各子程序的用途:

ABORT(sid) 人为中止一个挂起的语句(调用者需是会话属主,或拥有 ALTER SYSTEM/DBA 权限)

SET_TIMEOUT(n) 修改当前会话的超时时间,立即生效

GET_SESSION_TIMEOUT / SET_SESSION_TIMEOUT 查看 / 修改指定会话的超时时间

SPACE_ERROR_INFO 只能在 AFTER SUSPEND 触发器里调用,返回挂起原因的详细信息

SPACE_ERROR_INFO 有两个容易踩坑的点:一是它的参数里没有错误号,错误号要从 DBA_RESUMABLE.ERROR_NUMBER 拿;二是 ERROR_TYPE 的取值是三类字符串之一 ——NO MORE SPACE 、 MAX EXTENTS REACHED 、 SPACE QUOTA EXCEEDED 。

 

实测:完整复现一次挂起与恢复

1. 准备一个 5M 的小表空间(DBA)
SQL> create tablespace small_tbs datafile '/oracle/app/oradata/orcl/small01.dbf' size 5m;
 
Tablespace created.
2. 给测试用户授权(DBA)
 
sql
SQL> grant resumable to test;
 
Grant succeeded.
3. 建表并开启可恢复模式(测试用户)
 
sql
SQL> create table t_resumable(id number, pad varchar2(4000)) tablespace small_tbs;
 
Table created.
 
SQL> alter session enable resumable timeout 600 name 'fill_small_tbs';
 
Session altered.
4. 制造空间耗尽
 
sql
SQL> begin
  2    for i in 1 .. 10000 loop
  3      insert into t_resumable values (i, rpad('x', 4000, 'x'));
  4    end loop;
  5    commit;
  6  end;
  7  /
插入几千行之后 5M 空间耗尽。如果没开可恢复模式,这里会立刻报 ORA-01653 并把整个 PL/SQL 块的插入全部回滚;现在这个会话只是安静地停在那里,一直运行到把空间补上为止。
 
5. 另一个会话观测挂起状态(DBA)
 
sql
SQL> select session_id, name, status, timeout, error_number, error_msg
  2  from dba_resumable;
 
SESSION_ID NAME              STATUS      TIMEOUT ERROR_NUMBER
---------- ----------------- ----------- ------- ------------
ERROR_MSG
----------------------------------------------------------------
       137 fill_small_tbs    SUSPENDED       600         1653
ORA-01653: unable to extend table TEST.T_RESUMABLE by 128 in tablespace SMALL_TBS
 
SQL> select sid, event from v$session_wait where sid = 137;
 
       SID EVENT
---------- ------------------------------------------------------------
       137 statement suspended, wait error to be cleared
此时告警日志里也会记录一条形如 "statement in resumable session 'User TEST(...)' was suspended due to ..." 的消息。
 
6. 解决空间问题(DBA)
 
sql
SQL> alter database datafile '/oracle/app/oradata/orcl/small01.dbf' resize 100m;
 
Database altered.
7. 见证奇迹
回到第 4 步的那个会话:什么也不用做,插入循环自动继续执行,直到 10000 行全部插完正常结束。DBA_RESUMABLE 中的 
RESUME_TIME 列会记录恢复时刻。如果 600 秒内没有补空间,该会话才会收到 ORA-01653,插入被回滚。


 

加一道保险:AFTER SUSPEND 触发器

挂起虽然" 静悄悄 " ,但总不能靠人肉轮询 DBA_RESUMABLE 。 Oracle 提供了专门的 AFTER SUSPEND 系统事件触发器,挂起发生的瞬间自动触发,非常适合用来记日志、发告警,甚至自动处理。

SQL> create table resumable_log(
  2    log_time        date,
  3    session_id      number,
  4    err_type        varchar2(30),
  5    object_owner    varchar2(128),
  6    object_type     varchar2(128),
  7    tablespace_name varchar2(128),
  8    object_name     varchar2(128),
  9    error_msg       varchar2(4000)
 10  );
 
Table created.
 
SQL> create or replace trigger trg_after_suspend
  2  after suspend on database
  3  declare
  4    v_err_type varchar2(30);
  5    v_obj_type varchar2(30);
  6    v_owner    varchar2(128);
  7    v_tbs      varchar2(128);
  8    v_obj      varchar2(128);
  9    v_sub_obj  varchar2(128);
 10  begin
 11    if dbms_resumable.space_error_info(
 12         v_err_type, v_obj_type, v_owner, v_tbs, v_obj, v_sub_obj) then
 13      insert into resumable_log(log_time, session_id, err_type, object_owner,
 14                                object_type, tablespace_name, object_name, error_msg)
 15      select sysdate, dr.session_id, v_err_type, v_owner, v_obj_type,
 16             v_tbs, v_obj, dr.error_msg
 17      from dba_resumable dr
 18      where dr.session_id = sys_context('userenv', 'sid');
 19      commit;
 20    end if;
 21  end;
 22  /
 
Trigger created.


关于这个触发器,官方文档说明了一个很关键的性质:触发器里的 SQL 永远是非可恢复的,并且以自治事务方式运行(使用 SYSTEM 回滚段)。也就是说它自己不会再因为空间问题挂起或死锁,可以放心地在里面写日志、发邮件( UTL_SMTP ),甚至可以在确认无法处理时调用 DBMS_RESUMABLE.ABORT 主动中止,或调用 SET_TIMEOUT 给 DBA 争取更多处理时间。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70013625/viewspace-3181346/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
Oracle ACE,MySQL 8.0 OCP,PMP,拥有OceanBase OBCP,TiDB PCTP,GoldenDB GDCP等多项认证,ITPUB认证专家,微信公众号:DBA之友。

注册时间:2022-02-08

  • 博文量
    132
  • 访问量
    171486