MySQL 8.0+ 仅支持通过 CREATE/ALTER USER ... WITH MAX_QUERIES_PER_HOUR 设置频率限流,按自然小时统计语句总数,不区分类型、不看耗时,不可自定义窗口;GRANT ... WITH 已废弃且逻辑危险,应禁用。

MySQL 8.0+ 怎么给用户加 MAX_QUERIES_PER_HOUR 限制
直接用 CREATE USER 或 ALTER USER 设置,这是唯一原生支持的“频率限流”方式。它不拦单条慢查询,只卡单位时间内的语句总数——适合防轮询、脚本误跑、爬虫扫库这类高频轻量请求。
实操建议:
-
CREATE USER 'api_user'@'%' WITH MAX_QUERIES_PER_HOUR 120;—— 新建用户时直接绑定,最干净 -
ALTER USER 'report_user'@'localhost' WITH MAX_QUERIES_PER_HOUR 300;—— 已有用户可随时调整,无需重连 - 该限制对所有语句生效(
SELECT/INSERT/UPDATE/DELETE/SET都算),但COMMIT、ROLLBACK、SHOW等不计入 - 注意:不是每秒或每分钟,是严格按“自然小时”滚动(从 00:00 开始计数),不能自定义窗口
为什么 GRANT ... WITH MAX_QUERIES_PER_HOUR 在 MySQL 8.0+ 不推荐用
这个语法在 5.7 是合法的,但在 8.0+ 已被废弃,且逻辑极容易踩坑:它只对当前 GRANT 语句中显式列出的权限生效,没列的权限完全不受限。比如你写 GRANT SELECT ON db.* TO u@h WITH MAX_QUERIES_PER_HOUR 100;,那用户后续如果还有 UPDATE 权限(来自另一条 GRANT),更新操作就完全绕过限制。
更糟的是,多个 GRANT 记录共存时,系统取的是各条里最大的值,不是累加,查问题时数值对不上,调试成本高。
所以:一律用 CREATE/ALTER USER ... WITH,别碰带 WITH 的 GRANT。
MAX_QUERIES_PER_HOUR 真的能防住大查询吗
不能。它只计数,不看执行时间、不看扫描行数、不看内存占用。一条 SELECT SLEEP(30) 算 1 次,一条 SELECT * FROM huge_table WHERE ... 也只算 1 次。真正耗资源的大查询,它完全不管。
如果你的目标是防拖垮数据库的长耗时查询,得换别的手段:
- 会话级设
SET SESSION max_execution_time = 5000;(单位毫秒),但需应用连接后主动执行,或靠中间件注入 - 用
RESOURCE GROUP(仅限 MySQL 8.0.19+,且只对显式绑定的SELECT生效) - 在代理层(如 ProxySQL、MaxScale)或应用网关做更细粒度的 QPS + 响应时间双控
连接池会让 MAX_QUERIES_PER_HOUR 失效吗
不会失效,但效果会打折扣。因为限制是按“用户身份”统计的,不是按连接。只要连接池复用的是同一个账号(比如 app_user),所有从池子里发出的语句都会累计到这个用户的小时额度里。
不过要注意:MAX_CONNECTIONS_PER_HOUR 才是专门管连接建立频次的,它限制的是 mysql_real_connect() 调用次数,对连接池场景更有意义——比如设成 10,那这用户一小时内最多只能新建 10 次连接,哪怕池子里只维持 2 个活跃连接,第 11 次建连也会被拒。
所以:高频短连接场景优先配 MAX_CONNECTIONS_PER_HOUR;稳定长连接+大量语句场景才靠 MAX_QUERIES_PER_HOUR。
真正难搞的从来不是怎么配这条语句,而是当业务突然增长、监控告警说“API 用户超频了”,你得快速判断:是真恶意刷量?还是新上线功能触发了未预料的轮询?抑或是连接池配置崩了导致疯狂重连?这时候光看 MAX_QUERIES_PER_HOUR 的报错没用,得立刻查 performance_schema.events_statements_summary_by_account_by_event_name 和慢日志。

















