MySQL触发器不能直接发HTTP请求,因其无原生网络I/O能力;需依赖UDF(如mysql-udf-http)或sys_exec调用外部命令,但存在同步阻塞、无超时重试、权限与并发风险;推荐采用Outbox模式——触发器仅写入outbox表,由外部服务轮询并可靠发送HTTP请求。
MySQL本身不提供网络I/O能力,http_get、http_post这类函数不是原生支持的,必须依赖UDF(用户自定义函数)扩展。但这些UDF本质是把curl或系统调用塞进mysqld进程里执行,同步阻塞、无超时、无重试——一旦下游接口卡住,你的INSERT就卡死,事务直接失败。
即使你成功编译安装了mysql-udf-http.so并注册了http_post,在触发器里也只能这么写:
SET @res = (SELECT http_post('https://api.example.com', '{"id": 123}'));
不能直接写SELECT http_post(...)(语法报错),也不能在存储过程中裸调用。常见坑包括:
CREATE FUNCTION时SONAME路径错一个字符(比如mysql-udf-http.so.0.0.0没软链成mysql-udf-http.so),就会报ERROR 1127: Can't find symbol 'http_post'
@RequestBody接收,http_post默认发application/x-www-form-urlencoded,必须手动拼query string,否则body为空NULL时不会抛异常,只静默失败;你得靠@res IS NULL判断,但无法区分是网络超时还是服务返回空用lib_mysqludf_sys的sys_exec跑curl或php脚本看似灵活,实则更危险:
mysqld进程用户(通常是mysql)决定,常因权限不足失败sys_exec返回的是shell退出码,不是HTTP状态码,0只代表命令跑完了,不代表请求成功max_connections或ulimit -n限制,导致DB连接夯住echo >> /tmp/debug.log硬写文件,线上环境通常禁止写盘别让触发器干网络的事。正确做法是触发器只写一张outbox表,比如:
CREATE TABLE outbox ( id BIGINT AUTO_INCREMENT PRIMARY KEY, event_type VARCHAR(32), payload TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, processed TINYINT DEFAULT 0);
然后用独立服务(Python/Go/Java)轮询该表,发HTTP请求,并更新processed=1。这个模式的关键点是:
outbox表里可查可重放,比UDF里“丢了就没了”强十倍真正麻烦的不是怎么调通一次HTTP,而是如何让通知这件事在生产环境里扛住流量、容错、可观测——这些全得甩给应用层,而不是塞进数据库里。