事情本身不复杂,线索都在日志里。站点每个页面都调用 session_start,同一个会话的并发请求被文件锁串行处理。首屏接口要发 6 个请求,最慢的一个等了 3 秒,TTFB 分位数被拉到 2.8 秒。
第一反应是「首屏并发请求过多,把等待时间放大」,验证下来不成立。
真正麻烦的地方在于,几个原因的现象几乎一样。session 文件锁在同一会话内互斥,请求只能排队。少数情况下是只读场景也调用了 session_start,没有及时 session_write_close。
不容易想到的是,接口里还有两次全表统计 每次请求都重新算一遍。
第一件事是确认 session 是否阻塞并发
curl -s -o /dev/null -w '%{time_starttransfer}\n' https://www.example.com/api/a & curl -s -o /dev/null -w '%{time_starttransfer}\n' https://www.example.com/api/b
只读接口提前释放会话锁
session_start(); $_SESSION['uid']; session_write_close();
再往下合并首屏接口减少并发
curl -s 'https://www.example.com/api/bundle?parts=a,b,c' -o /dev/null -w '%{time_total}\n'
复测首字节时间分位
ab -n 200 -c 20 'https://www.example.com/api/a' | grep 'Time per request'
恢复的判定其实很直接。提前释放会话锁并合并接口后,首屏最慢请求从 3 秒降到 240 毫秒,TTFB 分位数回落到 0.4 秒。
补一句。PHP 的 session 是文件锁串行的,只读接口拿到数据后立刻 session_write_close,这一行往往比换缓存中间件更立竿见影。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。
