mysql原生可可靠限制最大并发连接数(用max_user_connections,新建连接时实时校验并报error 1226),但无法真正实现“每小时查询次数”限流——max_queries_per_hour在5.7.6+默认不校验、8.0+彻底失效,实为累计计数器且不自动重置;真限流需proxysql或应用层redis+lua实现滑动窗口计数。

MySQL 原生能可靠限制最大并发连接数,但无法真正实现“每小时查询次数”限流——MAX_QUERIES_PER_HOUR 在 5.7.6+ 默认不校验,8.0+ 彻底失效,别白配。
用 MAX_USER_CONNECTIONS 控制并发连接数
这是唯一稳定、服务端实时生效的并发控制机制,每次新建连接时立即检查,超限直接报 ERROR 1226 (42000)。
- 新建用户时设置:
CREATE USER 'app'@'%' IDENTIFIED BY 'pwd' MAX_USER_CONNECTIONS 5; - 修改已有用户:
ALTER USER 'app'@'%' MAX_USER_CONNECTIONS 3;(注意:不是WITH,GRANT ... WITH MAX_USER_CONNECTIONS对已有用户无效) - 查当前配置:
SELECT User, Host, max_user_connections FROM mysql.user WHERE User = 'app';,返回NULL或0表示不限 - 连接池(如 HikariCP)会一次性建满 idle 连接,极易触达上限;监控应查
performance_schema.threads中user分组计数,而非只看应用日志
别信 MAX_QUERIES_PER_HOUR 能按小时限流
它名字像,行为不像:在 MySQL 5.7.6+ 默认跳过运行时检查,8.0+ 彻底移除逻辑,即使你 ALTER USER ... WITH MAX_QUERIES_PER_HOUR 100 成功,执行 101 次 SELECT 1 也大概率不报错。
- 验证是否真生效:用目标用户登录后循环执行
SELECT 1,观察是否出现ERROR 1226 (42000): User 'xxx' has exceeded the 'max_questions' resource - 即使旧版本(如 5.7.20)能触发,它也不是“每小时”,而是累计计数器——达到 100 后永远卡住,除非 DBA 手动
ALTER USER ... WITH MAX_QUERIES_PER_HOUR 0清零 -
SHOW GRANTS FOR 'user'@'%'看到配置 ≠ 生效;mysql.user表里max_questions有值 ≠ 服务端在检查
真要控每小时查询频次,必须绕开 MySQL 服务端
生产环境只有两条靠谱路径,选哪个取决于你能否引入中间件:
-
有 ProxySQL(推荐):在
mysql_query_rules中加规则匹配用户名 +^SELECT,配合 Lua 脚本读写 Redis 键user:app:hourly_qps,INCR并EXPIRE 3600,超阈值直接OK返回不转发 -
无 ProxySQL,只能靠应用层:每次查询前调用 Redis + Lua 原子脚本,例如:
EVAL "local c = redis.call('INCR', KEYS[1]); redis.call('EXPIRE', KEYS[1], 3600); return c" 1 user:app:hourly_qps,返回值 > 100 就拒绝请求 - 别用定时
ALTER USER清零——主从延迟、crontab 失败、脚本未执行都会导致配额漂移,监控对不上时间窗口
MAX_CONNECTIONS_PER_HOUR 和 MAX_UPDATES_PER_HOUR 是什么关系
它们和 MAX_USER_CONNECTIONS、MAX_QUERIES_PER_HOUR 全部独立计数,互不降级,任一超限就失败。
-
MAX_CONNECTIONS_PER_HOUR:只统计成功认证的mysql_real_connect()次数(即“新连上”动作),需用GRANT USAGE ON *.* TO 'u'@'h' WITH MAX_CONNECTIONS_PER_HOUR 60设置,且必须FLUSH PRIVILEGES -
MAX_UPDATES_PER_HOUR:仅限UPDATE语句,和MAX_QUERIES_PER_HOUR一样,在 5.7.6+ 默认不生效,8.0+ 彻底失效 - 三者叠加时,一个用户可能同时被“并发连接数满”“每小时连了 61 次”“第 101 次 SELECT”三条规则拦住,错误码都是
ERROR 1226,但含义不同,日志里得靠上下文区分
真正难处理的是时间窗口对齐:ProxySQL 或 Redis 的 TTL 是严格 3600 秒滑动窗口,而业务方常以为“自然小时”更合理;一旦监控告警按整点切片,但限流按请求到达时间算,就会出现误报或漏控。这个细节上线前必须和 SRE 对齐。











