Oracle 19c下如何确保同一会话中PROC_BEGIN与PROC_END更新日志表的同一条记录
嘿,这个坑我之前做ETL日志系统的时候踩过!用日期+过程名来匹配更新确实会在多用户并发的时候出问题——毕竟同一时间可能有好几个用户跑同一个存储过程,数据库根本分不清哪条日志属于哪个会话,自然就会出现重复更新或者更新错记录的情况。下面给你几个在Oracle 19c里实测靠谱的解决方案:
方法一:利用Oracle会话唯一标识(最省事的方案)
Oracle每个会话都有一个全局唯一的SESSIONID,可以通过SYS_CONTEXT('USERENV','SESSIONID')获取,这个值在会话存续期间不会变,而且不同用户的会话ID绝对不会重复,完美解决多用户并发的匹配问题。
步骤1:给日志表新增会话ID列
如果你的LOG_TABLE还没有这个字段,先执行新增:
ALTER TABLE USER_NAME.LOG_TABLE ADD SESSION_ID NUMBER NOT NULL;
步骤2:修改PROC_BEGIN,插入时带上会话ID
在PROC_BEGIN插入日志记录的时候,把当前会话ID也存进去:
INSERT INTO USER_NAME.LOG_TABLE ( ID_DATE, PROC_NAME, TABLE_NAME, PROC_START_TIME, SESSION_ID, -- 其他需要的列(比如RCRD_INSRT_DATE等) ) VALUES ( p_date, v_owner||'.'||v_caller, P_TABLE_NAME, v_start_time, SYS_CONTEXT('USERENV','SESSIONID'), sysdate -- 对应RCRD_INSRT_DATE );
步骤3:修改PROC_END的UPDATE语句,加入会话ID条件
把你原来的UPDATE语句加上SESSION_ID的判断,这样就能精准定位到当前会话插入的那条记录:
UPDATE USER_NAME.LOG_TABLE SET ROW_COUNT = v_row_count, PROC_END_TIME = v_end_time, PROC_NAME = v_owner||'.'||v_caller, RUN_TIME_SECS = (v_end_time - v_start_time )*24*60*60, RCRD_INSRT_DATE = sysdate WHERE ID_DATE = p_date AND PROC_NAME = v_owner||'.'||v_caller AND TABLE_NAME = P_TABLE_NAME AND SESSION_ID = SYS_CONTEXT('USERENV','SESSIONID'); -- 这行是核心!
这样一来,不管有多少用户同时跑同一个存储过程,每个会话只会更新自己插入的那条日志记录,完全不会串线。
方法二:自定义请求ID(更灵活的方案)
如果你的ETL流程需要跨会话追踪请求,或者不想依赖Oracle的会话ID,可以自己生成唯一的请求ID,通过参数传递给PROC_BEGIN和PROC_END:
步骤1:创建一个生成请求ID的序列
CREATE SEQUENCE LOG_REQUEST_SEQ START WITH 1 INCREMENT BY 1 NOCYCLE;
步骤2:在调用存储过程时生成请求ID并传递
修改你的PROC_DO_SOMETHING,先获取请求ID,再传给日志过程:
PROCEDURE PROC_DO_SOMETHING IS v_request_id NUMBER; -- 其他变量声明 BEGIN -- 生成唯一请求ID v_request_id := LOG_REQUEST_SEQ.NEXTVAL; -- 把请求ID传给PROC_BEGIN PKG_ETL_LOGGER.PROC_BEGIN(parameters, v_request_id); -- 执行你的业务代码... -- 把请求ID和行计数传给PROC_END PKG_ETL_LOGGER.PROC_END(parameters, v_request_id, v_row_count); END;
步骤3:修改日志过程的逻辑
PROC_BEGIN插入日志时,把v_request_id存入新增的REQUEST_ID列(需要先给LOG_TABLE加这个列)PROC_END更新时,WHERE条件加上REQUEST_ID = v_request_id,这样就能精准匹配到对应的日志记录
这个方案的好处是,就算会话意外断开,你也能通过请求ID追踪整个请求的日志,适合复杂的ETL调度场景。
关键提醒
别再用SYSDATE、过程名这类容易重复的字段作为匹配条件了!在多用户并发的场景下,这些字段的重复率极高,根本无法保证精准匹配。必须用会话级或请求级的唯一标识,才能从根源上解决重复记录的问题。
备注:内容来源于stack exchange,提问作者Gustafsson Newad

