必须显式授予EVENT权限且仅限全局(.),否则CREATE EVENT报错ERROR 1227;该权限不随DML权限自动赋予,也不在ALL PRIVILEGES中;MySQL 8.0+角色无法间接继承,需SET ROLE激活;事件执行依赖DEFINER权限,而非创建者权限。
必须显式授予 EVENT 权限,且只能作用于全局(*.*),否则 CREATE EVENT 会报错:ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER privilege(s) for this operation。
MySQL 的 EVENT 权限设计就是全局性的。哪怕你只想让账号管理 app_logs 库里的事件,也不能写成 GRANT EVENT ON `app_logs`.*——这会导致创建事件时直接拒绝,还可能误触发对 SUPER 权限的检查。
正确写法只有这一种:
GRANT EVENT ON *.* TO 'evt_runner'@'localhost';
EVENT 权限不随 SELECT/INSERT 等 DML 权限自动赋予,也不在 ALL PRIVILEGES 里(除非显式加 WITH GRANT OPTION)EVENT 授给角色,也必须用 SET ROLE 显式激活才生效DEFINER 账号的其他权限这是最常被忽略的权限断层:事件创建成功 ≠ 事件能执行。MySQL 只在校验创建时是否具备 EVENT 权限,运行时完全按 DEFINER 身份执行语句,而 DEFINER 的权限可能不足。
典型现象:
ENABLED,NEXT_EXECUTED 时间正常更新,但表数据毫无变化information_schema.EVENTS 发现 LAST_EXECUTED 为空或远早于当前时间解决方法:
DEFINER 指向的账号(如 DEFINER = 'evt_runner'@'localhost')确实拥有事件体中所有 SQL 所需的权限(比如 TRUNCATE 需要 DROP 权限,DELETE 需要 DELETE 权限)DEFINER——MySQL 默认用当前用户,但若账号被重命名或权限变更,行为会不可控SELECT USER(), CURRENT_USER(); 对照验证实际执行身份用 root 或应用主账号跑事件等于长期开着数据库后门。一个只读账号创建的事件,只要定义里写了 DROP TABLE mysql.user,它就能执行——只要创建时有 EVENT 权限。
安全建号步骤:
CREATE USER 'evt_runner'@'localhost' IDENTIFIED BY 'strong-pass-2026';
GRANT SELECT, INSERT, UPDATE ON `app_logs`.* TO 'evt_runner'@'localhost';
GRANT EVENT ON *.* TO 'evt_runner'@'localhost';
FLUSH PRIVILEGES;
ALL、不跨库SUPER、PROCESS、REPLICATION CLIENT——这些权限能让事件绕过多数安全限制DEFINER 决定,不是调用者,所以 DEFINER 账号越小越安全真正容易被忽略的点在于:权限边界在 CREATE EVENT 那一刻就锁死了。之后改账号权限、删账号、甚至改 DEFINER 字段(不通过 ALTER DEFINER),都不会影响已存在事件的执行身份和能力。