max_execution_time仅限select语句超时中断,对update/delete等无效,且默认为0、可被客户端覆盖,无法防御真实慢sql攻击。

max_execution_time 是什么,为什么它防不住真正的慢SQL攻击
max_execution_time 是 MySQL 5.7.8+ 引入的会话级超时参数,单位毫秒,作用是「强制中断执行时间超过阈值的 SELECT 语句」。但它只对 SELECT 生效,对 UPDATE、DELETE、INSERT ... SELECT 完全无效;而且默认为 0(不限制),即使你全局设了,客户端连接仍可覆盖它。真实攻击者发一条没加 LIMIT 的 UPDATE t1 JOIN t2 ON ... SET t1.x = 1,max_execution_time 就彻底失能。
常见错误现象:SET GLOBAL max_execution_time = 1000 后,监控里还是出现 30 秒的 UPDATE 占满 CPU。
- 它不阻塞连接建立,只干预正在执行的 SELECT
- 事务内语句超时后,事务不会自动回滚,可能留下脏状态
- PHP/Python 等客户端若未捕获
Query execution was interrupted错误,会静默失败或重试,反而加剧负载
真正有效的三道防线:从连接层到语句层
靠单一参数拦不住慢 SQL 攻击,得组合控制:
-
连接准入层:用
max_connections+wait_timeout防连接堆积,比如设wait_timeout = 60,避免空闲连接长期占着线程 -
资源限制层:给高风险账号配
MAX_STATEMENT_TIME(MySQL 8.0.12+)或MAX_EXECUTION_TIME(Percona Server),它比会话变量更可靠,且支持非 SELECT - 语句拦截层:在代理层(如 ProxySQL、MaxScale)配置规则,直接拒绝无 WHERE 的
UPDATE/DELETE,或匹配JOIN+ORDER BY+ 无 LIMIT 的 SELECT
示例(ProxySQL):
INSERT INTO mysql_query_rules (active, match_pattern, error_msg) VALUES (1, 'UPDATE.*WHERE', 'No WHERE clause allowed');
为什么 set global max_execution_time 多数时候等于白设
因为应用几乎从不用 SET SESSION max_execution_time = ...,而 SET GLOBAL 只影响后续新连接的默认值,已有连接和多数 ORM(如 Django、Laravel Eloquent)根本不会读这个值。更麻烦的是,MySQL 的 max_execution_time 在主从复制中不传递,从库可能照常执行超长语句,导致主从延迟飙升。
- Java 的 JDBC 连接串加
sessionVariables=max_execution_time=1000才生效,但很多项目没配 - MySQL 5.7 不支持
MAX_STATEMENT_TIME,升级前别指望靠它控 UPDATE - 超时触发的错误码是
ER_QUERY_INTERRUPTED(1317),不是所有监控工具都识别它
上线前必须验证的两个细节
一是确认你的 MySQL 版本是否真支持你要用的功能:运行 SELECT VERSION(),再查 SHOW VARIABLES LIKE 'max_execution_time';二是测试超时后连接是否真的释放——有些场景下线程卡在 Waiting for table metadata lock,KILL QUERY 都杀不掉,得靠 KILL CONNECTION。
容易被忽略的地方:慢 SQL 攻击往往伴随大量小查询打满网络或连接数,而不是单条巨慢语句。所以 max_execution_time 再准,也救不了 max_connections = 1000 被刷爆的实例。盯死 Threads_running 和 Aborted_connects,比调参重要得多。











