首页 > 数据库 > Oracle > 怎么让业务程序在排查中变得清晰易懂
排查数据库问题时, 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/ ,如需转载,请注明出处,否则将追究法律责任。