mysql 8.0+ 原生不支持每小时查询次数限制,max_questions为累计阈值且不自动重置;需在应用层用redis+lua原子计数,或通过proxysql等代理层实现。

MySQL 8.0+ 中如何配置用户级查询速率限制
MySQL 原生支持基于用户的每小时查询次数限制,但仅限于 SELECT 语句(不包含 INSERT/UPDATE/DELETE),且必须使用 CREATE USER 或 ALTER USER 的 MAX_QUERIES_PER_HOUR 选项。5.7 及更早版本不支持该功能。
- 该限制统计的是客户端发起的 *所有*
SELECT语句(含子查询、视图查询),但不统计预处理语句的重复执行(只计首次解析) - 计时窗口是“滚动一小时”:不是从整点开始,而是从用户第一次触发查询起,往后推 60 分钟为一个周期
- 超出限制后,后续
SELECT会直接报错:ERROR 1226 (42000): User 'u_test' has exceeded the 'max_questions' resource (current value: 100) - 限制对存储过程内部的
SELECT也生效,但对函数内联查询是否计数取决于执行上下文,建议实测验证
创建带查询频率限制的新用户
必须在创建时显式指定,不能靠后续 GRANT 添加:
CREATE USER 'api_reader'@'10.20.%.%' IDENTIFIED BY 's3cure_p@ss' WITH MAX_QUERIES_PER_HOUR 300;
注意:WITH 子句必须紧跟在 IDENTIFIED BY 后,不能放在 GRANT 语句里;IP 段通配符用 %,不能写成 10.20.*.*。
修改已有用户的查询上限
对已存在用户,用 ALTER USER 覆盖原值(设为 0 表示不限制):
ALTER USER 'report_user'@'localhost' WITH MAX_QUERIES_PER_HOUR 60;
常见疏漏:
- 忘记加
@'host'部分,导致修改了错误的账户(如'user'和'user'@'localhost'是两个独立用户) - 执行后未
FLUSH PRIVILEGES—— 实际上不需要,ALTER USER会立即生效 - 误以为限制对所有语句生效,结果
SHOW TABLES或EXPLAIN仍可高频执行(它们不算SELECT资源)
验证与排查限制是否生效
检查用户当前配额:
SELECT user, host, max_questions FROM mysql.user WHERE user = 'api_reader';
注意:max_questions 字段值即为 MAX_QUERIES_PER_HOUR 设置值;但该字段不会动态更新已消耗次数 —— MySQL 不提供实时余量查询接口。
调试技巧:
- 用
mysql -u api_reader -p -hxxx连接后,循环执行SELECT SLEEP(0.1);快速触达阈值,观察报错时机 - 若始终不触发限制,确认连接使用的账号名和 host 是否与
mysql.user中完全一致(包括空格、大小写) - 代理层(如 ProxySQL、MaxScale)可能缓存连接或重写语句,导致 MySQL 层看到的用户与预期不符
真正难控的是应用层并发连接数和长连接复用行为——限制按用户维度而非连接维度,多个连接共享同一个每小时配额。这点常被忽略。











