必须由应用层显式传值:set @current_user_id = 'u_12345',触发器通过ifnull(@current_user_id, 'unknown')安全读取;session_user()、user()、current_user()均返回连接池账号,无法获取真实业务用户。

不能靠 SESSION_USER()、USER() 或 CURRENT_USER() 直接拿到业务用户——它们返回的全是连接池账号,不是张三李四。
为什么触发器里 USER() 和 CURRENT_USER() 都不准
MySQL 触发器运行在数据库会话上下文中,所有函数都只反映「谁连进来」,不反映「谁发起这次修改」。比如应用用统一账号 app@10.0.0.5 建立连接池,那无论哪个前端用户操作,CURRENT_USER() 永远返回这个值。
SESSION_USER() 和 USER() 行为基本一致,只是语义略有差异,但解决不了连接池穿透问题。代理用户(proxy user)机制极少被 Web 应用采用,别指望它能救场。
-
CURRENT_USER()返回权限生效身份(适合审计权限边界) -
USER()返回认证时用户名(适合记录连接来源) - 两者在直连场景下结果相同,但都不等于业务用户 ID
必须由应用层显式传值:SET @current_user_id
MySQL 不提供类似 PostgreSQL 的 current_setting(),但支持会话级变量 @xxx,只要应用在每次 DML 前设置,触发器就能读取。
应用侧需确保每条业务 SQL 执行前执行:
SET @current_user_id = 'u_12345'; SET @client_ip = '192.168.1.100';
触发器中直接使用,**不要 SELECT**:
NEW.audit_user_id := IFNULL(@current_user_id, 'unknown');
- 变量作用域仅限当前连接,不会跨请求污染
- 漏设会导致
@current_user_id为NULL,务必用IFNULL()兜底 - DBA 直连或脚本调用时容易漏设,建议日志中同时记录
CURRENT_USER()作辅助参考
别试图从 general_log 或 PROCESSLIST 里捞当前 SQL 用户
有人想查 mysql.general_log 或 information_schema.PROCESSLIST 取最近一条语句的用户,这条路走不通:
-
general_log是异步写入,触发器执行时看到的可能是其他连接的日志 -
PROCESSLIST中的user列仍是连接账号,不是业务用户 - 高并发下时间戳对齐误差大,无法可靠绑定「这条 UPDATE 对应哪个用户」
- 开启
general_log本身会显著拖慢性能,生产环境禁用
这些方法只适用于低频调试,不能作为审计字段的稳定来源。
真正可靠的路径只有一条:应用控制写入时机 + 触发器安全兜底。漏设变量比函数选错更常见,检查点要落在应用层是否每次请求都重置了 @current_user_id。











