doctrine dbal连接超时需分阶段设置:连接建立用options下1002(pdo::attr_timeout),查询执行用1003(pdo::attr_statement_timeout);但doctrine:database:create命令不生效,须结合数据库服务端wait_timeout、php-fpm进程回收及连接健康检查综合优化。

Doctrine DBAL 连接超时参数怎么设
Doctrine 默认不主动设置连接超时,MySQL 或 PostgreSQL 的底层驱动会用各自默认值(比如 MySQL 默认 wait_timeout=28800 秒),但这个值对 Web 请求毫无意义——你真正要控制的是「建立连接」和「执行查询」两个阶段的等待上限。
在 config/packages/doctrine.yaml 中显式配置超时参数:
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
options:
# 连接建立阶段最大等待时间(秒)
1002: 5 # PDO::ATTR_TIMEOUT → 对应 MySQL 的 connect_timeout
# 查询执行阶段最大等待时间(秒)
1003: 30 # PDO::ATTR_STATEMENT_TIMEOUT → MySQL 8.0.12+ / PostgreSQL 支持
注意:1002 和 1003 是 PDO 常量的整数值,不能写成 PDO::ATTR_TIMEOUT;不同数据库支持程度不同:MySQL 8.0.12+ 才认 1003,老版本只能靠应用层兜底。
为什么 php bin/console doctrine:database:create 不触发超时设置
这个命令走的是 Doctrine 的 SchemaTool 流程,绕过了 DBAL 的连接池和部分选项加载逻辑,options 里的超时参数不会生效。它只依赖 DATABASE_URL 中的基础连接信息和底层驱动默认行为。
- 遇到卡住或长时间无响应,先手动测试连通性:
telnet db-host.internal 3306 - 确认数据库服务本身没被防火墙拦截、DNS 解析正常(别用
localhost) - 若需强制限制建库操作耗时,改用 shell 命令加
timeout:timeout 10s php bin/console doctrine:database:create --if-not-exists
云环境里超时问题常和连接池/空闲回收冲突
阿里云 RDS、腾讯云 CDB 等托管数据库通常主动 kill 掉空闲超过 300 秒的连接(wait_timeout)。而 Symfony 默认复用 PDO 连接,如果某次请求结束后连接没被及时关闭,下次再取出来用就可能抛出 MySQL server has gone away。
解决办法不是调高超时,而是让连接更“健壮”:
- 在
.env的DATABASE_URL后追加&connect_timeout=5&read_timeout=10&write_timeout=10(MySQL 协议层参数,PDO 层不一定透传,但部分驱动会识别) - 启用 Doctrine 的连接健康检查:
doctrine.dbal.ping_before_connect: true(Symfony 6.2+,旧版需自定义 Connection 类) - 生产环境禁用
auto_generate_proxy_classes,避免冷启动时大量反射拖慢首次连接
PHP-FPM 子进程生命周期影响连接复用效果
超时配置再准也没用,如果 PHP-FPM 的 pm.max_requests = 0 或设得极大,一个子进程可能持续运行数小时,中间持有的 PDO 连接早已被数据库端断开。此时下一次 $em->getRepository()->findAll() 就会失败。
推荐做法:
- 把
pm.max_requests设为 500–1000,让子进程定期重启,自然释放陈旧连接 - 不要在 service 或 controller 里长期持有
Connection实例,每次需要都从EntityManagerInterface拿 - 监控日志里是否高频出现
SQLSTATE[HY000] [2006] MySQL server has gone away,这是连接空闲过久的典型信号
超时从来不是单点参数能解决的事——它横跨 PDO 驱动、数据库服务配置、容器网络策略、PHP 运行模型四层。改一个 1003 数值只是开始,后面每层都得对得上。











