从其他存储过程调用调试存储过程时无日志插入问题排查
核心问题:事务回滚导致调试数据被撤销
直接调用sp_debug能写入数据,说明这个存储过程本身没问题。问题出在sp_taskComplete的事务机制上——MySQL里存储过程的所有操作默认在同一个隐式事务里,只要事务中出现未处理的异常,整个事务的所有修改都会被回滚,包括sp_debug插入的调试记录。
具体触发场景
查询无匹配数据时的异常
当SELECT creator_id INTO taskCreatorId from tasks WHERE id = in_taskId;执行时,如果传入的in_taskId在tasks表中不存在,会触发NO_DATA_FOUND错误,直接回滚整个事务,之前插入的调试数据自然就没了。自定义异常触发回滚
当in_userId != taskCreatorId时,你用SIGNAL抛出了异常,这个操作会立即终止存储过程,同时回滚整个事务,之前调用sp_debug插入的记录被一起撤销。而且注意,SIGNAL之后的SET out_returnCode := -1根本不会执行。嵌套存储过程的异常传递
如果sp_checkCompleteSubtasks内部抛出了未处理的异常,这个异常会传递到sp_taskComplete,同样触发主事务回滚,清除所有调试数据。
解决办法
1. 捕获查询异常,避免自动回滚
给SELECT ... INTO添加异常处理逻辑,比如:
BEGIN DECLARE taskCreatorId bigint unsigned; -- 添加无数据的异常处理 DECLARE CONTINUE HANDLER FOR NOT FOUND BEGIN call sp_debug('ERROR', '传入的taskId不存在', 'sp_taskComplete -2'); SET out_returnCode := -2; END; call sp_debug( 'in_taskId', 'in_taskId::', 'sp_taskComplete -1'); SELECT creator_id INTO taskCreatorId from tasks WHERE id = in_taskId; -- ... 后续代码不变 END;
2. 抛出异常前提交调试数据
如果需要保留触发SIGNAL前的调试记录,可以在抛出异常前显式提交事务:
IF (in_userId != taskCreatorId) THEN call sp_debug('ERROR', '当前用户不是任务创建者', 'sp_taskComplete -4'); COMMIT; -- 先提交调试数据,再抛出异常 SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Only creator can complete task !'; END IF;
3. 让sp_debug独立提交(谨慎使用)
修改sp_debug,在插入后强制提交,让调试数据脱离主事务:
CREATE PROCEDURE `TasksWithSearch`.`sp_debug`( IN in_label varchar(100), IN in_value varchar(1000), IN in_source varchar(100) ) BEGIN INSERT INTO data_debug(label, value, source) VALUES(in_label, in_value, in_source); COMMIT; -- 强制提交,确保调试数据不被主事务回滚 END
⚠️ 注意:这个方法会打破主存储过程的事务一致性,如果主逻辑需要原子性操作,不建议用。
4. 检查嵌套存储过程的异常处理
确保sp_checkCompleteSubtasks内部处理了自身的异常,不要让异常传递到主存储过程导致回滚。
内容的提问来源于stack exchange,提问作者Petro Gromovo

