MySQL 的 max_execution_time 仅对单条 SELECT 生效且需显式开启,对写操作无效;PostgreSQL 的 statement_timeout 更可靠,支持多级配置并主动中断连接;网关层需配合读超时与主动 cancel 才能真正防止单查询阻塞全局。
MySQL 中 max_execution_time 是 per-statement 的,不是全局网关级的
它只对单条 select 语句生效,且必须显式开启(默认为 0,即不限制),不能靠它防住 update、delete 或存储过程里的长耗时逻辑。网关层面根本收不到这个信号——mysql 自己杀掉查询后,连接可能还挂着,客户端甚至收不到完整错误。
-
max_execution_time只影响SELECT,对写操作完全无效 - 需在语句前加
/*+ MAX_EXECUTION_TIME(3000) */提示,或设会话变量SET SESSION max_execution_time = 3000 - 该参数不继承自全局,每个新连接都要重设;用连接池时尤其容易漏
- 超时后 MySQL 返回
Query execution was interrupted错误,但 TCP 连接未断,网关可能继续等待响应
PostgreSQL 用 statement_timeout 更可靠,但得配对应用层重试
它对所有语句类型都生效(SELECT/INSERT/UPDATE/DELETE),且支持会话级、用户级、数据库级三级配置,比 MySQL 灵活得多。但注意:超时触发的是 canceling statement due to statement timeout,PostgreSQL 会主动关闭后端进程,连接立刻中断。
- 推荐在
postgresql.conf中设statement_timeout = 5000(毫秒),再用ALTER ROLE ... SET statement_timeout = 3000覆盖特定用户 - 应用必须捕获
SQLSTATE 57014并区分是真失败还是可重试超时;盲目重试可能引发重复写入 - 如果用了 PgBouncer,要确认运行在
transaction模式而非session模式,否则SET不生效
网关层真正能兜底的只有连接/读超时 + 主动 cancel
像 Nginx、Envoy、Spring Cloud Gateway 这类网关,无法识别 SQL 内容,只能基于 HTTP 生命周期做控制。真正的“防止单一 SQL 阻塞全局”必须靠两层配合:网关设硬性读超时,后端服务在收到网关断连信号后立即 cancel 当前查询。
- Nginx 示例:
proxy_read_timeout 8s,配合proxy_next_upstream error timeout http_500 - Java 应用需监听
ServletRequest.getAsyncContext().addListener(...)或 Spring 的@EventListener(RequestTimeoutEvent.class),触发connection.cancel() - Go 的
http.Server.ReadTimeout不够,必须用context.WithTimeout传入 DB 查询,并确保 driver 支持 context cancel(如pgx支持,database/sql+lib/pq已废弃)
别忽略连接泄漏和慢查询日志联动
光设超时没用。如果一个查询被 kill 后连接没归还给池子,或者慢查询本身没被记录下来,你永远不知道哪个服务、哪类 SQL 在拖垮整体。必须让超时行为留下可观测痕迹。
- MySQL 开启
log_slow_slave_statements和long_query_time = 0.5,配合pt-query-digest定期分析 - PostgreSQL 开启
log_min_duration_statement = 500,并确保log_line_prefix包含%p(进程 ID)和%i(SQL ID),方便关联 cancel 日志 - 所有数据库连接池(HikariCP / pgxpool / SQLAlchemy engine)必须设
leak_detection_threshold,超时未归还连接直接报错,而不是静默重连
最麻烦的不是设不设超时,而是超时之后谁来清理连接、谁来记录上下文、谁来通知告警。这三个动作缺一不可,少一个就等于没设。

















