在MySQL数据库开发中,存储过程常用于封装复杂的业务逻辑,当存储过程包含多层条件判断、循环操作或者跨表数据交互时,调试难度会大幅上升。传统的客户端调试工具对存储过程的支持有限,很难直观看到每一步的执行状态。

一、设计存储过程日志记录表
首先需要创建一张专用的日志表,用来存储存储过程执行过程中的关键信息,表结构需要覆盖执行时间、存储过程名称、当前执行步骤、变量值、异常信息等核心维度。
-- 创建存储过程日志记录表
CREATE TABLE proc_exec_log (
log_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '日志ID',
proc_name VARCHAR(100) NOT NULL COMMENT '存储过程名称',
step_desc VARCHAR(200) COMMENT '当前执行步骤描述',
step_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '步骤执行时间',
var_info TEXT COMMENT '关键变量值信息',
error_msg VARCHAR(500) COMMENT '异常错误信息,无异常则为空',
exec_status TINYINT NOT NULL COMMENT '执行状态:1正常 0异常'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='存储过程执行日志表';
二、在存储过程中插入日志记录逻辑
在存储过程的关键节点,比如入口、条件分支判断前、循环开始/结束、变量赋值后、异常捕获块中,插入向日志表写入记录的语句,就能完整记录执行轨迹。
2.1 基础日志记录存储过程封装
为了避免重复编写插入日志的SQL,可以先封装一个通用的写日志存储过程,所有业务存储过程都调用这个公共过程记录日志。
-- 通用存储过程日志记录过程
DELIMITER //
CREATE PROCEDURE write_proc_log(
IN p_proc_name VARCHAR(100),
IN p_step_desc VARCHAR(200),
IN p_var_info TEXT,
IN p_error_msg VARCHAR(500),
IN p_exec_status TINYINT
)
BEGIN
INSERT INTO proc_exec_log (proc_name, step_desc, var_info, error_msg, exec_status)
VALUES (p_proc_name, p_step_desc, p_var_info, p_error_msg, p_exec_status);
END //
DELIMITER ;
2.2 业务存储过程集成日志示例
下面是一个包含条件判断和循环的复杂存储过程示例,演示如何在关键节点插入日志记录。
-- 示例业务存储过程:处理用户订单积分更新
DELIMITER //
CREATE PROCEDURE update_user_order_point(
IN p_user_id INT,
IN p_order_id INT,
OUT p_result INT
)
BEGIN
-- 定义变量
DECLARE v_order_amount DECIMAL(10,2);
DECLARE v_point_rate DECIMAL(5,2) DEFAULT 0.1;
DECLARE v_total_point INT;
DECLARE v_error_msg VARCHAR(500) DEFAULT '';
-- 声明异常捕获
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
v_error_msg = MESSAGE_TEXT;
-- 记录异常日志
CALL write_proc_log('update_user_order_point', '捕获到异常',
CONCAT('user_id:', p_user_id, ',order_id:', p_order_id),
v_error_msg, 0);
SET p_result = -1;
END;
-- 记录存储过程入口日志
CALL write_proc_log('update_user_order_point', '存储过程开始执行',
CONCAT('user_id:', p_user_id, ',order_id:', p_order_id), '', 1);
-- 查询订单金额
SELECT order_amount INTO v_order_amount FROM order_info
WHERE order_id = p_order_id AND user_id = p_user_id LIMIT 1;
-- 记录查询订单结果日志
CALL write_proc_log('update_user_order_point', '查询订单金额完成',
CONCAT('order_amount:', v_order_amount), '', 1);
-- 条件分支判断
IF v_order_amount IS NULL THEN
CALL write_proc_log('update_user_order_point', '订单不存在',
CONCAT('order_id:', p_order_id), '未找到对应订单记录', 0);
SET p_result = -2;
ELSEIF v_order_amount < 100 THEN
CALL write_proc_log('update_user_order_point', '订单金额不足100,不计算积分',
CONCAT('order_amount:', v_order_amount), '', 1);
SET p_result = 0;
ELSE
-- 计算积分
SET v_total_point = FLOOR(v_order_amount * v_point_rate);
CALL write_proc_log('update_user_order_point', '积分计算完成',
CONCAT('order_amount:', v_order_amount, ',point_rate:', v_point_rate, ',total_point:', v_total_point), '', 1);
-- 更新用户积分
UPDATE user_info SET total_point = total_point + v_total_point
WHERE user_id = p_user_id;
CALL write_proc_log('update_user_order_point', '用户积分更新完成',
CONCAT('user_id:', p_user_id, ',add_point:', v_total_point), '', 1);
SET p_result = 1;
END IF;
-- 记录存储过程结束日志
IF v_error_msg = '' THEN
CALL write_proc_log('update_user_order_point', '存储过程执行结束',
CONCAT('result:', p_result), '', 1);
END IF;
END //
DELIMITER ;
三、通过日志排查存储过程问题
当存储过程执行结果不符合预期时,只需要查询proc_exec_log表,按照step_time排序,就能看到完整的执行轨迹。
-- 查询指定存储过程的最新执行日志 SELECT log_id, step_desc, step_time, var_info, error_msg, exec_status FROM proc_exec_log WHERE proc_name = 'update_user_order_point' ORDER BY step_time DESC LIMIT 20;
通过日志可以直观看到:如果exec_status为0的日志记录,说明对应步骤出现了异常,直接看error_msg就能定位错误原因;如果流程没有走到预期的分支,看对应条件判断步骤的var_info就能知道当时的变量值为什么不符合预期;如果循环执行次数不对,看循环开始和结束的日志时间差和变量值就能找到问题。
四、注意事项
- 日志表需要定期清理,避免大量存储过程执行日志占用过多磁盘空间,可以设置定时任务删除超过30天的历史日志。
- 生产环境可以设置日志开关,通过一个全局变量控制是否记录日志,避免正常执行时产生不必要的性能开销。
- 日志的
var_info字段不要记录敏感信息,比如用户密码、身份证号等内容,避免信息泄露。 - 如果存储过程执行频率很高,建议给
proc_exec_log表的proc_name和step_time字段添加索引,提升日志查询效率。