Doctrine连接超时参数必须在config/packages/doctrine.yaml的doctrine.dbal.options中显式配置,如PDO::ATTR_TIMEOUT、PDO::MYSQL_ATTR_READ_TIMEOUT等,不能写在DATABASE_URL中。

Doctrine 连接超时参数写在哪
超时配置不在 doctrine.yaml,也不在 .env 的 DATABASE_URL 里直接填;它得通过 doctrine.dbal.options 显式传给底层 PDO。Symfony 默认不暴露这个入口,必须手动加。
在 config/packages/doctrine.yaml 中补全 options 块:
doctrine:
dbal:
options:
# 连接建立阶段超时(单位:秒)
PDO::ATTR_TIMEOUT: 10
# 查询执行阶段超时(MySQL 8.0.3+ 支持,需驱动配合)
PDO::MYSQL_ATTR_READ_TIMEOUT: 30
PDO::MYSQL_ATTR_WRITE_TIMEOUT: 30注意:PDO::ATTR_TIMEOUT 是 PDO 层通用参数,对 MySQL/PostgreSQL 都生效;后两个是 MySQL 特有,老版本 MySQL 或旧版 php-mysql 扩展可能忽略。
为什么 DATABASE_URL 里不能设 timeout
DATABASE_URL 解析后只映射基础连接参数(host、port、user、dbname 等),timeout 不是标准 URL 查询参数,Doctrine 的 UrlConnectionUrlHelper 会直接丢弃它。试过在 DATABASE_URL 后加 &connect_timeout=10 或 ?timeout=5 —— 没用,日志里看不到任何 effect。
- 错误写法:
mysql://u:p@127.0.0.1:3306/db?serverVersion=8.0&connect_timeout=10 - 正确路径:必须走
options,且值为整数(单位秒),不能带单位字符串 - 若用 Docker 或云数据库,网络延迟高,建议把
PDO::ATTR_TIMEOUT设到 15~30,避免容器启动时因 DB 尚未就绪而崩溃
连接超时和查询超时的区别与坑
很多人以为设了 PDO::ATTR_TIMEOUT 就能控制 SQL 执行时间,其实不是:它只管“连上数据库”这一步,一旦连接建立成功,后续 SELECT 或 INSERT 卡住几十秒,它完全不管。
真正控查询超时得靠数据库侧配合:
- MySQL:需
PDO::MYSQL_ATTR_READ_TIMEOUT+ MySQL 服务端wait_timeout/max_execution_time双重限制 - PHP 层无法单靠 PDO 参数中断已发出的查询,超时后连接可能进入不可用状态,Doctrine 会抛
Doctrine\DBAL\Exception\ConnectionException - 如果用 Doctrine QueryBuilder 写长耗时聚合查询,建议额外加
->setTimeout(20)(仅限部分驱动支持),或改用原生 SQL +executeStatement()手动控制
验证超时是否生效的最简方法
别依赖日志——Doctrine 默认不打连接超时细节。直接用控制台命令触发一次强制重连:
php bin/console doctrine:database:create --if-not-exists
然后人为制造超时场景测试:
- 临时关掉 MySQL:
sudo systemctl stop mysql,再运行命令,看是否在 10 秒后报Connection refused而非卡住 60 秒 - 把
PDO::MYSQL_ATTR_READ_TIMEOUT设成 1,跑一个SLEEP(2)查询:php bin/console doctrine:query:sql "SELECT SLEEP(2)",应立刻报错而不是等完 - 注意:某些云数据库(如 AWS RDS)会在连接层做代理,实际超时可能被截断或延长,得结合
tcpdump抓包确认
真正难调的是跨网络环境下的组合超时行为——PDO 参数、MySQL server 变量、云厂商 LB 超时、PHP-FPM request_terminate_timeout,四者嵌套时,最小的那个才起效。别只改 Symfony 配置就以为万事大吉。


















