Oracle VPD无侵入追踪INSERT操作调用栈的方案咨询
Oracle 19c追踪INSERT操作完整调用栈方案
(a) 优化现有VPD策略函数获取完整调用栈
之前的策略函数仅能拿到匿名调用信息,是因为没用到适配Oracle 19c的调用栈获取工具。12.1及以上版本支持的UTL_CALL_STACK包可以直接抓取完整嵌套调用链,包含所有层级的存储过程、函数和匿名块。
修改策略函数ING_PIM_DOC_CAT_PLY_FN的核心逻辑,替换原调用栈获取代码,示例如下:
DECLARE l_depth PLS_INTEGER; l_owner VARCHAR2(128); l_name VARCHAR2(128); l_line PLS_INTEGER; l_call_stack CLOB := EMPTY_CLOB(); BEGIN -- 获取调用栈层级深度 l_depth := UTL_CALL_STACK.Dynamic_Depth; -- 遍历每一层调用栈,收集所有者、程序名、行号信息 FOR i IN 1..l_depth LOOP l_owner := UTL_CALL_STACK.Owner(i); l_name := UTL_CALL_STACK.Concat_Subprogram(i); l_line := UTL_CALL_STACK.Line(i); l_call_stack := l_call_stack || CHR(10) || '层级 ' || i || ': ' || l_owner || '.' || l_name || ' (行号 ' || l_line || ')'; END LOOP; -- 将调用栈写入临时表t1,同时记录操作时间和用户 INSERT INTO t1 (call_stack, event_time, username) VALUES (l_call_stack, SYSTIMESTAMP, USER); -- VPD策略需返回过滤条件,这里返回1=1不影响INSERT执行 RETURN '1=1'; END;
注意事项:
- 给策略函数的执行用户授予
UTL_CALL_STACK包的执行权限:GRANT EXECUTE ON SYS.UTL_CALL_STACK TO <your_user>; - VPD策略在INSERT执行前触发,此时抓取的调用栈能完整反映触发INSERT的嵌套调用链路,不会遗漏层级。
(b) 无需修改核心代码的其他追踪方式
方案1:AFTER INSERT触发器+UTL_CALL_STACK
直接给目标表创建后置INSERT触发器,每次插入时自动抓取调用栈并存入持久化日志表,完全不触碰核心业务代码:
- 创建持久化日志表(替代临时表t1,避免数据丢失):
CREATE TABLE insert_call_stack_log ( log_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, call_stack CLOB, event_time TIMESTAMP WITH TIME ZONE, username VARCHAR2(128), inserted_row_id ROWID );
- 创建触发器:
CREATE OR REPLACE TRIGGER trg_track_insert_call_stack AFTER INSERT ON <your_target_table> FOR EACH ROW DECLARE l_depth PLS_INTEGER; l_call_stack CLOB := EMPTY_CLOB(); BEGIN l_depth := UTL_CALL_STACK.Dynamic_Depth; FOR i IN 1..l_depth LOOP l_call_stack := l_call_stack || CHR(10) || '层级 ' || i || ': ' || UTL_CALL_STACK.Owner(i) || '.' || UTL_CALL_STACK.Concat_Subprogram(i) || ' (行号 ' || UTL_CALL_STACK.Line(i) || ')'; END LOOP; INSERT INTO insert_call_stack_log (call_stack, event_time, username, inserted_row_id) VALUES (l_call_stack, SYSTIMESTAMP, USER, :NEW.ROWID); END; /
该方案简单直接,对业务性能影响极小(间歇性操作几乎无感知),只要有表的触发器创建权限即可实施。
方案2:Oracle SQL跟踪+TKPROF
如果需要更全面的执行上下文信息,可启用SQL跟踪分析INSERT语句的调用链:
- 查询目标INSERT语句的SQL_ID:
SELECT sql_id, sql_text FROM V$SQL WHERE sql_text LIKE 'INSERT INTO <your_target_table>%';
- 针对该SQL_ID启用跟踪:
EXEC DBMS_MONITOR.SQL_TRACE_ENABLE(sql_id => '<your_sql_id>', waits => TRUE, binds => FALSE);
- 待间歇性INSERT发生后,关闭跟踪:
EXEC DBMS_MONITOR.SQL_TRACE_DISABLE(sql_id => '<your_sql_id>');
- 用
TKPROF工具处理跟踪文件(跟踪文件路径可通过SELECT value FROM V$PARAMETER WHERE name='user_dump_dest'查询):
tkprof <trace_file_name>.trc output.txt explain=<user>/<password>
打开生成的output.txt文件,即可查看该INSERT语句的完整调用栈和执行路径。
方案3:OEM性能分析(有权限时)
如果拥有Oracle Enterprise Manager(OEM)权限,可直接在OEM性能页面定位目标表的INSERT语句,通过可视化界面查看其执行计划和调用链信息,无需编写任何代码。
内容的提问来源于stack exchange,提问作者Piyush
相关产品推荐
相关产品推荐

