最直接准确的PHP请求耗时统计方式是用microtime(true)在脚本开头和结尾各取一次时间戳并相减,单位为秒、精度达微秒;避免使用time()或Web服务器日志字段,因其精度不足或包含非PHP执行时间。

用 microtime(true) 记录 PHP 请求耗时最直接
PHP 原生没有自动记录单次请求耗时的开关,得自己埋点。最可靠、开销最小的方式就是用 microtime(true) 获取浮点秒级时间戳,在脚本开头和结尾各调一次,相减即得真实耗时(单位:秒,精度到微秒)。
注意别用 time() ——它只精确到秒,对监控没意义;也别依赖 Apache 或 Nginx 的日志字段(如 %D),它们统计的是整个 HTTP 生命周期,包含网络传输、队列等待等,不是 PHP 实际执行时间。
- 开头记录:
$start = microtime(true); - 结尾计算:
$elapsed = microtime(true) - $start; - 建议在
index.php入口最顶部写$start,在所有逻辑跑完、响应发出前(比如echo或exit前)算$elapsed
把耗时写进日志或监控系统要注意格式和性能
直接 error_log("req_time: {$elapsed}s"); 很简单,但高频请求下频繁 IO 会拖慢响应。更稳妥的做法是:先拼好日志字符串,再统一写入(比如用 file_put_contents(..., FILE_APPEND | LOCK_EX)),或走异步通道(如 syslog、UDP 发给 StatsD / Prometheus Pushgateway)。
如果用 Monolog 等日志库,记得加 extra 字段传耗时,别塞进 message 主体里——否则后续用 ELK 或 Loki 做聚合分析会很麻烦。
立即学习“PHP免费学习笔记(深入)”;
- 推荐日志字段名:
duration_ms(单位毫秒,整数,方便排序和告警阈值设置) - 避免写成:
"耗时:0.123456 秒"这类带单位、空格、中文的字符串 - 若用
$_SERVER['REQUEST_URI']记录路径,记得过滤敏感参数(如token=xxx),防止日志泄露
CLI 脚本和 Web 请求的耗时统计逻辑要分开处理
Web 请求有明确生命周期(从接收请求到发送响应),而 CLI 脚本可能长期运行、多任务并行。不能直接复用同一套计时逻辑。
例如 Laravel 的 Artisan 命令,应在 handle() 方法开头记 $start,结尾算差值;如果命令里调用了多个子任务(如循环处理 100 条数据),还应单独记录每个子任务耗时,避免“总耗时长但不知道哪块慢”。
- CLI 下可加
register_shutdown_function()保底兜底,防止异常退出导致没记录 - Web 场景慎用
register_shutdown_function()记耗时——它可能在输出缓冲已关闭后执行,导致无法写日志或发请求 - 若用了 Swoole 或 RoadRunner,
microtime(true)依然有效,但需确认是否在协程上下文里调用(Swoole 1.9+ 的Co::getMicroTime()更准确)
用 Xdebug 或 Blackfire 做深度耗时分析时别混淆层级
microtime 给你的是总耗时,但查瓶颈得知道哪行函数拖慢了。Xdebug 的 profiler 或 Blackfire 的火焰图能定位到具体函数调用栈,不过默认配置下它们会显著拖慢性能(Xdebug 开启 profiler 后请求可能慢 3–5 倍)。
线上环境绝对不要常开,建议只在预发或通过开关动态启用(比如加个 ?debug=perf 参数触发)。
- Xdebug 配置关键项:
xdebug.mode=profile+xdebug.start_with_request=trigger - Blackfire 需配合浏览器插件或
blackfire curl命令手动触发,生成的 profile 链接有时效性,别误当永久指标 - 注意:Xdebug 3.1+ 默认禁用
var_dump()对性能的影响,但 profiler 本身仍是重操作,勿与opcache.enable=1同时用于压测
实际部署时,很多人漏掉的一点是:没把耗时指标和请求上下文(如用户 ID、接口名、HTTP 状态码)绑定。光有数字没用,必须能按接口、按错误类型、按用户分组下钻,才真正具备监控价值。











