laravel中mysql连接超时需配置pdo::mysql_attr_connect_timeout等参数,而非pdo::attr_timeout;读写超时无法在主从间分开设,php版本和mysqlnd驱动影响实际生效,系统tcp超时可能覆盖php设置。

MySQL 连接超时参数在 Laravel 中怎么配
连接超时(connect timeout)和读写超时(read/write timeout)不是 Laravel 自己控制的,而是由底层 PDO 驱动传给 MySQL 客户端库的。Laravel 只负责把配置项透传过去,关键得看 pdo_mysql 扩展是否支持、MySQL 服务端是否接受这些选项。
常见错误现象:SQLSTATE[HY000] [2002] Connection refused 或卡住十几秒才报错,其实是 PHP 默认的 socket 超时(默认 60 秒)在起作用,不是 Laravel 配置没生效。
-
'options' => [PDO::ATTR_TIMEOUT => 5]对 MySQL 无效 —— 这是 SQLite 用的,PDO 不会把它转成 MySQL 的connect_timeout - 真正起效的是
PDO::MYSQL_ATTR_CONNECT_TIMEOUT、PDO::MYSQL_ATTR_READ_TIMEOUT、PDO::MYSQL_ATTR_WRITE_TIMEOUT,必须显式写进database.php的options - PHP 版本影响大:PHP 7.4+ 才稳定支持
PDO::MYSQL_ATTR_READ_TIMEOUT;低版本即使写了也忽略 - Linux 系统级限制可能覆盖 PHP 设置,比如
/etc/my.cnf里connect_timeout=2会导致 PHP 设置失效
Laravel 主从配置下读写超时能分开设吗
不能。Laravel 的读写分离是靠 Connection 实例动态切换 host,但每个连接实例的超时参数是在第一次 pdo->connect() 时就固定的,不支持“读连接用 3 秒,写连接用 10 秒”这种运行时区分。
你看到的主从配置里写两套 options,其实只有主库(write)的生效,从库(read)会复用主库的 PDO 实例配置,除非你手动为从库单独建连接。
- 主库配置里的
PDO::MYSQL_ATTR_CONNECT_TIMEOUT会影响所有连接(含从库首次连接) - 从库如果走的是同一个 PDO 实例(默认行为),它的读操作超时实际由 MySQL 服务端的
wait_timeout和网络层决定,PHP 层无法干预单次SELECT的读取耗时 - 想实现读操作提前中断,只能靠应用层加
DB::timeout(3)->table(...)->get(),但这只是语句级超时,不是 socket 级
超时设置后为什么还是卡住 30 秒才报错
因为 MySQL 协议本身有握手阶段、认证阶段、查询执行阶段,而 PHP 的 PDO::MYSQL_ATTR_*_TIMEOUT 只控制其中一部分。最常被忽略的是 TCP 层的 connect() 调用,它受操作系统 socket 默认超时影响。
典型场景:主库宕机、DNS 解析失败、防火墙拦截 SYN 包 —— 这些情况下,PHP 会等系统返回 ETIMEDOUT,Linux 默认是 30~60 秒,跟 Laravel 配置完全无关。
- 验证方法:用
telnet bad-host 3306看卡多久,就是你的真实 connect 超时底线 - 解决方向:在
/etc/sysctl.conf调小net.ipv4.tcp_syn_retries,或改用mysqlnd驱动(比 libmysql 更快失败) - Laravel 9+ 的
mysqlnd默认启用,但如果你编译 PHP 时没加--with-mysqlnd,还是走旧驱动,timeout 行为不可控
生产环境建议怎么设值
别迷信“统一设成 3 秒”。主库写操作要容忍慢盘、锁等待;从库读操作要防备大表扫描、复制延迟。硬设过短反而引发大量重试和雪崩。
更务实的做法是分层控制:连接阶段用较短超时(如 3 秒),执行阶段依赖 MySQL 自身的 max_execution_time(需 5.7.8+),再配合应用层 circuit breaker。
- 推荐配置:
'options' => [ PDO::MYSQL_ATTR_CONNECT_TIMEOUT => 3, PDO::MYSQL_ATTR_READ_TIMEOUT => 10, PDO::MYSQL_ATTR_WRITE_TIMEOUT => 10, ] - 必须搭配 MySQL 服务端配置:
wait_timeout = 60、interactive_timeout = 60、max_execution_time = 5000 - 注意:Laravel 的
DB::statement('SET max_execution_time=5000')每次查询都得重设,开销不小,建议直接改全局变量
超时不是开关,是权衡。网络抖动、慢查询、主从延迟——三者混在一起时,光调 PHP 参数没用,得看慢日志、抓包、查复制状态。最容易被忽略的是 mysqlnd 驱动是否真在跑,而不是你以为的“装了就行”。











