doctrine:query 命令可测连接建立与查询总耗时,输出的 time 值包含 tcp 握手、认证及执行三阶段;首次执行偏慢(>200ms)属正常,后续因连接复用而稳定;需禁用连接池(dsn_options={"pool":false})或清缓存后测试真实建连延迟。

用 doctrine:query 命令测单次连接建立耗时
Doctrine 自带的 doctrine:query 命令能绕过 ORM 映射,直接执行原生 SQL 并打印执行时间,适合测“连接建立 + 查询响应”总延迟。它不加载实体、不触发事件,比 doctrine:schema:validate 更轻量。
- 执行
php bin/console doctrine:query:sql "SELECT 1",输出里会带Time: xxx ms—— 这个值包含 TCP 握手、认证、查询执行三段耗时 - 如果第一次执行慢(比如 >200ms),但后续稳定在
- 别用
SELECT *测——结果集大小会影响耗时,干扰连接判断;固定用SELECT 1或SELECT NOW() - 若报错
SQLSTATE[HY000] [2002] No such file or directory,说明 Doctrine 尝试走 Unix socket(localhost)但路径不对,改DATABASE_URL中的 host 为127.0.0.1
Stopwatch 打点到 Connection::connect() 真实入口
想精确分离“连接建立”和“查询执行”,必须把 Stopwatch 打在 Doctrine 的底层连接逻辑上,而不是 Repository 方法里。因为 $em->getRepository()->find() 里可能复用已有连接,测不到真实建连开销。
- 在服务中注入
Connection实例:use Doctrine\DBAL\Connection;,然后调用前 start:$stopwatch->start('db_connect', 'database'); - 显式触发连接:
$connection->connect();,再 lap:$stopwatch->lap('db_connect'); - 注意:不要在循环里反复
connect()——DBAL 默认复用连接,手动调用反而破坏连接池,导致额外开销 - 如果
db_connect耗时 >100ms 且每次都不稳定,重点查 DNS 解析(host是域名而非 IP)、MySQL 认证插件兼容性(如mysql_native_password是否启用)
对比本地 vs 容器网络路径的 RTT 差异
云服务器或 Docker 环境下,连接慢往往卡在网络层,而非数据库配置。直接用 telnet 或 nc 测裸 TCP 建连时间,能快速定位是否是网络问题。
- 在 Symfony 应用所在机器执行:
time echo "" | nc -w 2 your-db-host 3306 2>/dev/null,看 real 时间。超过 100ms 就异常 - 如果是 Docker Compose 环境,应用没容器化,而 DB 在容器里:检查
/etc/hosts是否手动映射了db到容器 IP;否则 DNS 解析失败会卡满 timeout(默认 3 秒) - 云服务器场景下,确认安全组放行了 3306 端口,且数据库绑定地址不是
127.0.0.1(应为0.0.0.0或内网 IP) - 别依赖
ping——MySQL 端口不通时 ping 可能仍通,必须测端口级连通性
避免被连接池缓存掩盖真实延迟
Doctrine 默认启用连接池(doctrine.dbal.pooling),首次连接慢,后续复用快。这会让测试失真——你以为连得快,其实只是复用了旧连接。
- 临时禁用连接池测基线:在
.env里加DSN_OPTIONS={"pool":false},让每次connect()都走真实 TCP 握手 - 或者清空连接池再测:
php bin/console cache:clear --no-warmup,再立刻跑doctrine:query - 生产环境不能关池,但调试时关掉才能暴露真实建连瓶颈;如果关池后仍 wait_timeout 过短导致连接频繁断开重连)
- MySQL 侧检查:
SHOW VARIABLES LIKE 'wait_timeout';,建议设为 28800(8 小时),避免客户端因超时重连引发抖动











