hyperf中mysql查询超时需服务端max_execution_time、客户端i/o超时及连接池空闲策略三者协同;db::timeout(5)仅作用于query builder链式调用,对db::select()或swoole原生驱动无效,必须显式配置pdo attr_timeout、read_timeout及max_execution_time hint(仅select生效,需process权限且格式为/+ max_execution_time(3000) /)。

Hyperf 中 MySQL 查询超时中断不是靠客户端“设个 timeout 就行”,而是必须服务端 max_execution_time + 客户端协程 I/O 超时 + 连接池空闲策略三者对齐,缺一不可;否则语句卡住、协程挂起、QPS 归零。
为什么 DB::timeout(5) 对原生查询无效
Hyperf 的 DB::timeout() 是 Query Builder 层的封装,只作用于 DB::table()->where(...)->get() 这类链式调用。一旦你写的是 DB::select('SELECT /*+ MAX_EXECUTION_TIME(3000) */ * FROM ...') 或直接用 Swoole\Coroutine\MySQL,这个 timeout 就完全不生效。
真正起效的只有两处:
- MySQL 服务端的
MAX_EXECUTION_TIMEhint(仅 SELECT)或会话级max_execution_time - Swoole 协程 MySQL 客户端自身的
connect_timeout/read_timeout(需在连接配置中显式传入)
Hyperf 默认没透传这些底层超时,所以你得手动补上。比如在 config/autoload/db.php 的 options 里加:
'options' => [
\PDO::ATTR_TIMEOUT => 3, // PDO 层执行超时(秒),影响 prepare/execute
// 若用 Swoole 原生驱动,还需额外 set read_timeout:
'read_timeout' => 5.0,
'write_timeout' => 5.0,
],
MAX_EXECUTION_TIME 提示在 Hyperf 里怎么写才不被忽略
这个 hint 必须满足三个条件才会触发中断,否则静默失效:
- 语句必须是
SELECT——INSERT/UPDATE/DELETE完全不认 - 当前会话要有
PROCESS权限(云数据库如阿里云 RDS 默认关闭,需 DBA 开启) - 不能处于锁等待中:比如等行锁、MDL 锁,
MAX_EXECUTION_TIME的计时器照常走,但实际 SQL 并没执行,超时后报错却是Query execution was interrupted,容易误判
正确写法示例:
DB::select('SELECT /*+ MAX_EXECUTION_TIME(3000) */ id, name FROM users WHERE status = ?', [1]);
注意:注释格式必须是 /*+(带加号),不能写成 /* 或 /*!;单位是毫秒,且只对当前语句生效。
Hyperf 连接池 max_idle_time 和 MySQL wait_timeout 怎么配才不翻车
这是最常配错的一组参数。现象是:连接池里看着有连接,一取出来就报 MySQL server has gone away。
根本原因:max_idle_time(Hyperf 池内连接最大空闲秒数)必须严格小于 MySQL 的 wait_timeout(服务端断连阈值),建议差值 ≥ 30 秒:
- 查 MySQL 当前值:
SELECT @@wait_timeout;(别只看SHOW VARIABLES,它显示的是全局默认值) - 若 MySQL 返回
60,Hyperf 配置里就要设pool.max_idle_time = 30.0 -
pool.heartbeat不是“心跳开关”,它只在连接取出时单次 PING;真正在后台轮询的是IdleConnectionChecker线程,它依赖max_idle_time触发
配反的后果:
-
max_idle_time > wait_timeout:连接在池里“睡过头”,取出来必死 -
max_idle_time ≈ wait_timeout:边缘情况频繁触发重连,日志刷屏
慢查询卡死协程时,KILL QUERY 为什么不起作用
在 Hyperf + Swoole 场景下,一条没索引的 SELECT 可能导致整个 Worker 进程卡住,此时即使你在 MySQL 里执行 KILL QUERY xxx,也大概率无效。常见原因:
- SQL 正在执行大事务回滚(
ROLLBACK不可中断,InnoDB 硬限制) - 结果集已算完,正往客户端 socket 缓冲区发数据(
State: Sending data)——此时 kill 只断连接,服务端仍要塞完数据 - 用了 UDF 或自定义存储引擎插件,且代码里没定期检查
thd->killed标志位 - Swoole 协程 MySQL 客户端底层是同步阻塞封装,一旦
recvfrom阻塞,协程就无法让出 CPU,调度器瘫痪
这类场景下,max_execution_time 同样无效,因为它只在 SQL 执行阶段计时,不覆盖结果传输或回滚过程。唯一可靠办法是:提前建好索引、避免长事务、拆分大查询。











