mysql不提供sql级异常捕获机制,必须依赖客户端处理、存储过程handler或performance schema监控;错误日志不记录普通sql失败,通用日志仅记录原始语句,最可靠方案是组合客户端日志、慢查询日志与performance schema事件监控。

MySQL客户端执行SQL时如何捕获异常
MySQL本身不提供类似编程语言的try...catch机制,异常捕获必须依赖客户端(如Python、Java、PHP)或存储过程内的错误处理逻辑。直接在命令行或mysql客户端敲SQL时,异常只显示在终端,不会自动记录到日志文件。
常见现象:执行INSERT INTO t VALUES (1/0)会报ERROR 1365 (22012): Division by 0,但这条错误不会进任何MySQL服务端日志——除非你主动开启相关日志。
- 命令行下用
mysql -e "SQL"执行时,可通过$?检查退出码(非零即失败) - 在存储过程中,可用
DECLARE ... HANDLER捕获特定SQLSTATE或错误码,例如DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' - 应用层务必检查执行返回值或异常对象,不能只看SQL是否“看起来跑完了”
开启MySQL通用查询日志(含错误语句)
通用日志(general_log)会记录所有到达MySQL服务器的语句(包括语法错误的),但它不区分成功/失败,且默认关闭,开启后对性能有明显影响,仅建议临时排障。
启用方式(需有SUPER权限):
SET GLOBAL general_log = ON; SET GLOBAL log_output = 'file'; -- 或 'table',写入mysql.general_log表 SET GLOBAL general_log_file = '/var/log/mysql/general.log';
注意:general_log记录的是“收到的原始SQL”,不是执行结果;语法错误的SQL也会被记入,但执行阶段抛出的运行时错误(如唯一键冲突)不会额外标注为“异常”,需人工对照时间戳和后续返回信息判断。
- 日志路径需MySQL进程有写权限,否则启动失败且无提示
- 开启后日志体积增长极快,切勿长期开启
-
log_output = 'table'时,查SELECT * FROM mysql.general_log ORDER BY event_time DESC LIMIT 10可快速回溯
错误日志(error log)里能抓到哪些SQL异常
MySQL的错误日志(由log_error配置项指定)**不记录普通SQL执行失败**,它只记录服务启动、崩溃、连接异常、权限拒绝、表损坏等服务端级问题。像INSERT ... ON DUPLICATE KEY UPDATE中的主键冲突、UPDATE影响0行、甚至Deadlock found这类事务错误,默认也不会写入error log。
真正能进错误日志的SQL相关异常极少,典型如:
- 解析阶段失败:SQL语法严重错误导致mysqld无法初始化语句树(罕见)
- 权限类拒绝:
Access denied for user ... to database ... - 磁盘满导致
Failed to write to binlog进而中断SQL执行
所以别指望靠error log审计业务SQL失败原因——它不是为这个设计的。
真正可靠的SQL异常追踪方案
生产环境要稳定捕获SQL异常,必须组合使用客户端日志 + 慢查询日志 + 性能模式(Performance Schema)。
slow_query_log配合long_query_time = 0可记录所有SQL(含失败语句),但需注意:它只记录执行时间超限的语句,而很多异常(如语法错、权限错)根本没走到执行阶段,就不会被记录。
更实用的是启用Performance Schema中的语句事件监控:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_statements_history_long'; UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME = 'statement/sql/%';
之后查performance_schema.events_statements_history_long,字段SQL_TEXT、ERROR_CODE、ERROR_MESSAGE会明确标出失败语句及其错误详情。这是目前MySQL原生最接近“SQL异常日志”的能力。
- 该功能默认可能关闭,且消耗一定内存,需评估实例负载
-
ERROR_CODE为0表示成功,非0即失败(如1062是重复键,1213是死锁) - 历史记录滚动覆盖,需及时采集,不能依赖它长期存档
复杂点在于:没有单一开关能“打开SQL异常日志”。你得根据场景选工具——调试用general_log,运维监控靠performance_schema,应用层兜底靠客户端异常捕获。漏掉任意一环,异常就悄无声息地消失了。










