ITPub博客

首页 > 数据库 > Oracle > ​怎么让业务程序在排查中变得清晰易懂

​怎么让业务程序在排查中变得清晰易懂

原创 Oracle 作者:a960549548 时间:2026-09-11 15:18:06 0 删除 编辑

排查数据库问题时, DBA 打开 V$SESSION ,经常看到的是几百个长得一模一样的会话: PROGRAM 是 plus.exe 或 JDBC Thin Client , MODULE 一列基本都是默认值,分不清哪个会话属于哪个应用、哪个作业。批量作业更是一个黑盒:会话状态一直是 ACTIVE ,但执行到了哪一步、还要多久,应用之外无人知晓。

这个问题 Oracle 其实早有标准解法: DBMS_APPLICATION_INFO 包。它允许应用在运行过程中把自己的信息 写 到会话上, V$SESSION 直接可见;其中的 SET_SESSION_LONGOPS 过程还能借用 V$SESSION_LONGOPS 视图发布作业进度。该包的 EXECUTE 权限授予了 PUBLIC ,任何用户都可以直接使用,不需要额外授权。

一、给会话打标签: SET_MODULE / SET_ACTION / SET_CLIENT_INFO

三个过程分别对应 V$SESSION 的三列:  

SQL> exec dbms_application_info.set_module('夜间数据清理作业', '清理订单历史表')
PL/SQL procedure successfully completed.
SQL> exec dbms_application_info.set_action('删除 2024 年之前的订单')
PL/SQL procedure successfully completed.
SQL> exec dbms_application_info.set_client_info('批次号:20260911')
PL/SQL procedure successfully completed.

在另一个会话中查询,效果立即生效

 SQL> select sid, module, action, client_info from v$session
  2  where module = '夜间数据清理作业';
       SID  MODULE        ACTION              CLIENT_INFO
---------- ------- -----------------  ------------------------ --------------------
       137  夜间数据清理作业  删除 2024 年之前的订单   批次号:20260911

三列都有长度限制: MODULE 最长 48 字节, ACTION 最长 32 字节, CLIENT_INFO 最长 64 字节。语义上, MODULE 标识 哪个程序 , ACTION 标识 程序内当前哪一步 (一个模块内可多次调用 SET_ACTION 切换), CLIENT_INFO 放自由格式的附加信息。

两点需要说明:

其一, CLIENT_INFO 任何用户都可以读写,不要在其中存放需要保密的信息,这类需求应使用应用上下文( application context );

其二,包内提供了对应的读取过程 READ_MODULE 和 READ_CLIENT_INFO ,供程序自己取回当前标签。

MODULE 和 ACTION 会进入 SQL 关联视图( V$SQL 的 MODULE/MODULE_HASH/ACTION 列),可以按模块统计 SQL 的执行情况; DBMS_MONITOR 还支持按 service/module/action 组合开启跟踪和统计( SERV_MOD_ACT_TRACE_ENABLE 、 SERV_MOD_ACT_STAT_ENABLE ),为应用代码打上规范的标签后,针对性地开启 10046 跟踪就不再需要一个个找 SID 。

二、发布作业进度: SET_SESSION_LONGOPS 与 V$SESSION_LONGOPS

V$SESSION_LONGOPS 是 Oracle 内部使用的进度视图:并行查询、全表扫描、 RMAN 备份等预计超过 6 秒的操作都会在其中登记进度。 SET_SESSION_LONGOPS 过程把同一机制开放给应用程序——手工调用它,作业的进度也会出现在这个视图里, DBA 用惯用的 SQL 就能监控应用进度。

过程签名如下(参数为 IN 的部分省略了默认值):

DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS(

    rindex      IN OUT BINARY_INTEGER,

    slno        IN OUT BINARY_INTEGER,

    op_name     IN     VARCHAR2       DEFAULT NULL,

    target      IN     BINARY_INTEGER DEFAULT 0,

    context     IN     BINARY_INTEGER DEFAULT 0,

    sofar       IN     NUMBER         DEFAULT 0,

    totalwork   IN     NUMBER         DEFAULT 0,

    target_desc IN     VARCHAR2       DEFAULT 'unknown target',

    units       IN     VARCHAR2       DEFAULT NULL);

 

参数 说明
rindex 行标识。第一次调用前初始化为 set_session_longops_nohint(-1),调用后保存返回值,后续调用复用即可更新同一行
slno 与 rindex 配合的内部状态值,同样只初始化、复用,不手工修改
op_name 作业名,对应 OPNAME 列
target / target_desc 操作对象的编号及描述
sofar / totalwork 已完成量与总量,进度的核心字段
units sofar/totalwork 的单位,对应 UNITS 列


注意参数顺序: target 、 context 在 sofar 、 totalwork 之前,与直觉不同,用位置传参时容易踩错,建议一律使用命名传参。

使用上有几个已经过验证的行为特征:手工登记的行在第一次调用后立即可见,不受 6 秒阈值限制(该阈值只约束 Oracle 自动登记的操作); TIME_REMAINING 由 Oracle 根据已耗时间和 sofar/totalwork 比例推算, MESSAGE 列同样由系统自动拼出;作业完成后行不会删除,以 SOFAR = TOTALWORK 的状态保留在视图中,因此监控时应加 sofar != totalwork 过滤进行中的作业。

三、实测:一个可监控的批量作业

以下用 TEST 用户做一个完整演示。建表前确认当前用户在默认表空间上有配额( DBA 执行 alter user test quota unlimited on users; )。

1. 建演示表与存储过程(TEST 用户)
SQL> create table t_progress_demo(id number, pad varchar2(100));
Table created.
SQL> create or replace procedure p_progress_demo(p_total in number default 100000) as
  2    l_rindex binary_integer := dbms_application_info.set_session_longops_nohint;
  3    l_slno   binary_integer;
  4  begin
  5    dbms_application_info.set_module('数据加载作业', '初始化');
  6    for i in 1 .. p_total loop
  7      insert into t_progress_demo values (i, rpad('x', 100, 'x'));
  8      if mod(i, 5000) = 0 then
  9        dbms_application_info.set_session_longops(
 10          rindex    => l_rindex,
 11          slno      => l_slno,
 12          op_name   => '数据加载 t_progress_demo',
 13          sofar     => i,
 14          totalwork => p_total,
 15          units     => '行');
 16      end if;
 17    end loop;
 18    commit;
 19    dbms_application_info.set_action('done');
 20  end;
 21  /
Procedure created.
 
2. 执行作业(TEST 用户会话 A)
 
SQL> exec p_progress_demo(100000)
 
3. 另一会话监控(DBA)
 
SQL> select sid, module, action from v$session where module = '数据加载作业';
       SID MODULE                ACTION
---------- -------------------- --------
       137 数据加载作业          初始化
SQL> select sid, opname, sofar, totalwork,
  2         round(sofar/totalwork*100, 2) "percent",
  3         time_remaining, elapsed_seconds
  4  from v$session_longops
  5  where sofar != totalwork;
       SID  OPNAME          SOFAR    TOTALWORK  percent TIME_REMAINING ELAPSED_SECONDS
---------- ------- ----------------------- ---------- ---------- --------- -------------  -------------
       137 数据加载 t_progress_demo 60000    100000   60        9   14
 
从输出可以看到作业完成了60%,按当前速度推算还剩约9秒。作业结束后再查询,该行以SOFAR=TOTALWORK的状态保留。


  四、注意事项

1. 标签与进度行都随会话结束而消失,是运行时信息,不落库;

2. 使用连接池的应用在连接归还时应调用 SET_MODULE(NULL, NULL) 重置,否则下个请求会继承上一个请求的标签,监控数据失去意义;

3. SET_SESSION_LONGOPS 调用本身开销极小,但没必要每行调用一次,按批次(如每 5000 行)即可;

 


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

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

注册时间:2022-02-08

  • 博文量
    132
  • 访问量
    171486