应使用CURRENT_USER()获取真实用户,INSERT触发器中通过联合唯一索引日志表+NOW(3)实现防刷;UPDATE/DELETE触发器不适用会话级限频;生产环境应优先采用应用层限流。

触发器里怎么获取当前用户
MySQL 触发器本身不直接暴露会话用户信息,USER() 和 CURRENT_USER() 是唯一可用的函数,但要注意区别:USER() 返回「客户端声明的用户名@host」,可能被伪造;CURRENT_USER() 才是认证通过的实际账号,防刷必须用它。
常见错误是写成 SELECT USER() INTO @cur_user 然后做判断——这在 INSERT/UPDATE 触发器里能跑通,但值不可靠;更糟的是有人试图在触发器里调用 SESSION_USER(),这个函数在 MySQL 中根本不存在,会直接报错 ERROR 1305 (42000): FUNCTION xxx.SESSION_USER does not exist。
- 只信任
CURRENT_USER(),且必须在触发器体中直接使用,不要赋值给变量再引用(避免隐式类型转换干扰) - 如果应用层用了统一数据库账号(如 all_app@'%'),那
CURRENT_USER()就没意义了——得退回到应用层或代理层限频 - 注意权限:触发器执行时以定义者权限运行,确保定义者有查询
mysql.user或操作日志表的权限
INSERT 触发器里怎么记操作时间戳
不能依赖 NOW() 或 CURRENT_TIMESTAMP 直接插入日志表,因为并发高时多个语句可能拿到相同毫秒级时间,导致“同一秒内允许多次操作”的漏洞。
正确做法是把时间戳和用户绑定写入一张轻量日志表(比如 user_op_log),并用联合唯一索引强制去重或限制窗口:
CREATE TABLE user_op_log ( user_name VARCHAR(128) NOT NULL, op_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_name, op_time) ) ENGINE=InnoDB;
- 主键设为
(user_name, op_time),配合触发器里用VALUES(CURRENT_USER(), NOW(3))(NOW(3)带毫秒,降低冲突概率) - 如果要限制“每 5 秒最多 1 次”,就在触发器里查
SELECT COUNT(*) FROM user_op_log WHERE user_name = CURRENT_USER() AND op_time > DATE_SUB(NOW(3), INTERVAL 5 SECOND) - 别用
ON DUPLICATE KEY UPDATE回滚——触发器里不允许修改正在被触发的表,也不支持事务回滚语句,只能用SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Too frequent'
为什么 UPDATE/DELETE 触发器不适合做防刷
因为它们发生在行级,而防刷是会话级行为。一个 UPDATE t SET x=1 WHERE id IN (1,2,3,4,5) 会触发 5 次 BEFORE UPDATE,但你只想算“这个用户刚发起了一次批量操作”,不是五次。
更麻烦的是:触发器无法区分“用户主动发起”和“被其他逻辑连带触发”(比如外键级联、事件调度器任务),容易误杀。
- 防刷逻辑应集中在
INSERT到中间日志表,或统一收口到高频写入的业务表(如订单表、评论表)的INSERT触发器 - 如果必须监控 UPDATE,优先考虑在应用层加 Redis 计数器,或者用 MySQL 8.0+ 的
INFORMATION_SCHEMA.PROCESSLIST配合定期巡检(不推荐实时查,性能差) -
DELETE触发器几乎没法可靠防刷——用户删一行和删一千万行,在触发器里看到的都是单行上下文
触发器防刷的硬伤和替代思路
MySQL 触发器没有 sleep、没有异步、不能跨库锁表,一旦 SIGNAL 报错,整个语句就失败,用户体验就是“点提交突然弹窗报错”,没法提示“请 5 秒后再试”。
真正上线的系统,几乎没人纯靠触发器做防刷。它只适合临时应急、内部工具或低频管理操作。
- 生产环境首选应用层限流:Go 用
golang.org/x/time/rate,Java 用Guava RateLimiter,Python 用redis-py+ Lua 原子计数 - 如果必须数据库侧控制,MySQL 8.0 可用
RESOURCE GROUP限制 CPU 时间,或搭配max_execution_time终止长查询,但这不是频率控制 - 最常被忽略的一点:触发器日志表本身要定期清理(比如用事件
EVENT每小时删 5 分钟前的记录),否则磁盘和查询性能都会崩

















