max_queries_per_hour是mysql原生账户级限流机制,统计用户任意连续60分钟内所有语句总数,超限报错error 1226;设为0表示不限,需用create/alter user with配置,失效主因是连接池复用、隐式查询计入、未刷权限或拼写错误。

直接结论:用 CREATE USER 或 ALTER USER 配置 MAX_QUERIES_PER_HOUR 是唯一原生、有效的方式;设为 0 才是不限,不是留空或省略;它统计的是「该用户账号在任意连续 60 分钟内发起的所有语句总数」,超限立刻报错 ERROR 1226 (42000)。
为什么刚设完就失效?常见失效原因
这个参数看着简单,但实际中“设了等于没设”很常见,核心问题不在语法,而在执行上下文:
- 应用用了连接池(如 HikariCP、Druid)——
MAX_QUERIES_PER_HOUR按「用户身份」累计,不是按连接或会话。一个连接反复执行 300 次SELECT就会触发限流,和你开了 1 个还是 100 个连接无关 - ORM 或框架启动时的隐式查询(比如
SELECT DATABASE()、SHOW VARIABLES LIKE 'sql_mode')也会被计入额度,可能还没正式跑业务就耗掉几十次 - 如果误用
UPDATE mysql.user直接改表,必须手动执行FLUSH PRIVILEGES;而CREATE/ALTER USER不需要这步,漏了就白配 - 拼写错误:写成
MAX_QUERY_PER_HOUR(少 s)或MAX_QUESTIONS_PER_HOUR(旧别名,5.7+ 已弃用)会导致语法报错或静默忽略
CREATE USER 和 ALTER USER 的实操差异
两者效果完全一致,区别只在时机和权限要求:
- 新建用户时绑定最干净:
CREATE USER 'api_user'@'%' IDENTIFIED BY 'pwd' WITH MAX_QUERIES_PER_HOUR 300; - 已有用户随时可调:
ALTER USER 'report_user'@'localhost' WITH MAX_QUERIES_PER_HOUR 100; - 取消限制必须显式设为
0:ALTER USER 'user'@'host' WITH MAX_QUERIES_PER_HOUR 0;(设为NULL或不写,不会清空) -
ALTER USER要求你有SYSTEM_USER或ALTER USER权限;CREATE USER还需CREATE USER权限
它和 MAX_CONNECTIONS_PER_HOUR、MAX_UPDATES_PER_HOUR 的关系
它们互不干扰,各自独立计数,任一超限都会失败,没有降级逻辑:
-
MAX_QUERIES_PER_HOUR:统计所有语句(SELECT/INSERT/UPDATE/DELETE/SET),但COMMIT、ROLLBACK、SHOW不计入(注意:部分SHOW如SHOW TABLES会被计入,行为因版本略有差异) -
MAX_CONNECTIONS_PER_HOUR:只统计成功认证的mysql_real_connect()调用次数(即「新连上 MySQL」的动作),不能用ALTER USER改,必须用GRANT USAGE ON *.* TO 'u'@'h' WITH MAX_CONNECTIONS_PER_HOUR N;+FLUSH PRIVILEGES -
MAX_UPDATES_PER_HOUR:只计INSERT/UPDATE/DELETE/REPLACE,对读多写少的 API 场景意义有限,但适合防误删脚本
时间窗口不是滑动的,这点最容易被忽略
它不是“过去 60 分钟”,而是严格按 MySQL 服务端本地时间的自然小时滚动(从整点开始,比如 14:00–14:59:59),这意味着:
- 如果你在 14:59:30 设置限流为 100,那么第一次计数窗口是 15:00–15:59,前 30 秒的查询不会被纳入当前窗口
- 监控告警如果按客户端时间或 UTC 对齐,会发现阈值“跳变”或“延迟生效”,必须和服务端
SELECT NOW()时间一致 - 无法自定义窗口长度(比如改成每 5 分钟重置),MySQL 原生不支持滑动窗口或自定义周期











