MAX_UPDATES_PER_HOUR 是 CREATE/ALTER USER 语句中的语法子句,非系统变量或配置项;MySQL 8.0.12+ 已移除运行时校验,该限制实际不再生效,需应用层或 ProxySQL 实现限流。

MAX_UPDATES_PER_HOUR 是子句,不是配置项
MySQL 没有 max_updates_per_hour 这个系统变量,也不存在全局开关或 my.cnf 配置项。它只作为 CREATE USER 或 ALTER USER 语句里的语法子句存在,写进去才生效,不写就等于没限制。
常见错误是把它当成可动态设置的参数,比如执行 SET GLOBAL max_updates_per_hour = 100,这会直接报错 ERROR 1193 (HY000): Unknown system variable 'max_updates_per_hour'。
- 新建用户时绑定最稳妥:
CREATE USER 'api_user'@'%' IDENTIFIED BY 'pwd' WITH MAX_UPDATES_PER_HOUR 120; - 已有用户修改用:
ALTER USER 'api_user'@'%' WITH MAX_UPDATES_PER_HOUR 60; - 取消限制必须显式设为 0:
ALTER USER 'api_user'@'%' WITH MAX_UPDATES_PER_HOUR 0;(设为NULL或省略该子句,不会清空,而是保持原值或默认不限)
为什么设置了却没拦住 UPDATE?
不是语法错了,而是运行环境和权限模型导致“看似失效”。真正生效的前提是:用户没有 SUPER 权限,且连接走的是标准认证路径。
- 用
SHOW GRANTS FOR 'user'@'host';查权限,只要含SUPER,所有资源限制自动绕过 - 连接池(如 HikariCP、Druid)下多个连接共用同一用户名,计数按用户维度汇总,不是单连接独立算
- 计时窗口是自然小时(如 14:00–15:00),不是从第一次 UPDATE 开始滚动;超限后立刻报错
ERROR 1226 (42000): User 'xxx' has exceeded the 'max_updates_per_hour' resource - 拼写错误:
MAX_UPDATE_PER_HOUR(少 s)或旧别名MAX_UPDATES_PER_HOUR(5.7+ 已弃用)会导致静默忽略或语法错误
一条 UPDATE 算一次,不是按影响行数
这个限制统计的是「UPDATE 语句条数」,跟 WHERE 条件、IN 列表长度、影响行数完全无关。业务里如果习惯写 UPDATE t SET x=1 WHERE id IN (1,2,3,...,100),那这一条就算 1 次;但若拆成 100 条单 ID 更新,就消耗 100 次额度——几分钟就耗尽。
- 批量更新场景下,务必评估单条语句的“语句粒度”,而不是“数据粒度”
-
INSERT、DELETE、REPLACE各自需单独用MAX_INSERTS_PER_HOUR、MAX_DELETES_PER_HOUR等子句控制,不能混用 - 存储过程内部的
UPDATE不计入调用者的额度,只算最外层语句
MySQL 8.0+ 实际是否还生效?
MySQL 8.0 彻底移除了对 mysql.user 表中 max_updates_per_hour 字段的运行时校验逻辑。虽然 CREATE USER ... WITH MAX_UPDATES_PER_HOUR N 语法仍被接受(向后兼容),但服务端不再检查该字段,也就不会触发限流。
这意味着:如果你用的是 MySQL 8.0.12+,即使执行了 ALTER USER,也不会真正拦截 UPDATE。验证方式很简单——用该用户反复执行 UPDATE t SET x=x+1 WHERE id=1;,观察是否出现 ERROR 1226。大概率不会。
真正能落地的方案只剩两个:在应用层用 Redis + Lua 做原子计数(按 user + hour key 自增并 EXPIRE 3600),或引入 ProxySQL,在 query rules 层做基于用户名和时间窗口的硬拦截。原生机制在 8.0+ 已基本形同虚设。


















