MySQL触发器无法直接发邮件或调用外部通知,只能轻量写入alarm_log表;预警需靠定时脚本轮询+外部Webhook实现;PG可用NOTIFY/LISTEN异步通知;SQL Server可结合SQL Agent与Database Mail。

MySQL 触发器不能直接发邮件或调用外部通知
触发器里写 SELECT ... INTO OUTFILE 或调用 SYS_EVAL 是行不通的——MySQL 8.0+ 默认禁用这类危险函数,且触发器上下文不支持网络 I/O。真要“自动预警”,得把触发器当信号灯,只做最轻量的事:往一张监控表里插一条记录。
常见错误现象:ERROR 1419 (HY000): You do not have the SUPER privilege and binary logging is enabled,这是试图在触发器里调用含副作用的存储过程时触发的权限/日志限制。
- 触发器只负责写入
alarm_log表,字段至少包含:table_name、event_type(INSERT/UPDATE/DELETE)、trigger_time、detail_json - 避免在触发器里查大表或做 JSON 构造,否则拖慢主业务 DML;
detail_json推荐用JSON_OBJECT()简单拼几个关键字段 - 别用
AFTER INSERT去查刚插入的行再发通知——那不是“自动”,是阻塞式耦合,一出错整个事务就滚
用定时任务轮询 + 外部脚本发通知更可靠
真正落地的预警链路是:触发器埋点 → 定时脚本查新记录 → 调用企业微信/钉钉 Webhook 发消息。这个外部脚本可以是 Python、Shell,甚至用 mysql -e 配合 curl 一行搞定。
使用场景:比如订单表 orders 中 status = 'failed' 且 amount > 1000 时告警。
- 轮询频率建议设为 10–30 秒一次,太密会压数据库,太松延迟高;用
WHERE created_at > DATE_SUB(NOW(), INTERVAL 30 SECOND)查增量 - 查完立刻用
UPDATE alarm_log SET notified = 1 WHERE id IN (...)标记已处理,防止重复发 - Python 脚本里别用
time.sleep()硬等,改用schedule或 systemd timer 控制节奏
PostgreSQL 用户注意:NOTIFY/LISTEN 更适合实时通知
PG 的 NOTIFY 是轻量级异步信令机制,比轮询高效得多。触发器里执行 PERFORM pg_notify('sql_alarm', payload),再起一个常驻监听进程收消息发通知。
参数差异:pg_notify 的 payload 最大 8000 字节,超长得截断或转存到表里再发 ID。
- 监听端必须用
LISTEN sql_alarm启动连接,且该连接不能被短生命周期脚本反复新建销毁 - 不要在
NOTIFY里传敏感字段(如用户手机号),只传事件类型和记录 ID,详情由监听端按需查库 - 如果监听进程挂了,
NOTIFY消息就丢了——没有持久化队列,这点不如 Kafka 或 RabbitMQ
SQL Server 的替代方案:SQL Agent + Database Mail
SQL Server 原生支持更贴近“自动预警”的能力:触发器写入表 → SQL Agent Job 每分钟查一次 → 调用 sp_send_dbmail 发邮件。不用写外部脚本,但依赖 DB Mail 配置正确。
容易踩的坑:sp_send_dbmail 默认异步执行,返回成功不代表邮件真发出去了;错误日志藏在 sysmail_event_log 里,不是 ERROR_MESSAGE()。
- 务必在 Agent Job 步骤里加
IF @@ROWCOUNT = 0 RETURN,避免无数据时也触发发空邮件 - 邮件内容用
FOR XML拼 HTML 表格很常见,但要注意CHAR(13)+CHAR(10)换行在邮件客户端可能失效,优先用<br> - Database Mail 的账户密码是明文存进 msdb 的,权限控制要严格,别让普通开发账号能读
sysmail_account
复杂点在于跨系统通知——邮件只是兜底,真正要推送到飞书/企微,还是得靠外部服务中转;而所有方案里,触发器本身永远只该干一件事:留痕。别的,交给它该负责的层。

















