ThinkPHP 并发性能瓶颈源于同步阻塞模型及默认配置不合理,需优化 PHP-FPM 参数、关闭调试、避免同步 I/O、合理复用数据库连接、切换 Redis 缓存、规范缓存策略、引入队列异步处理非核心逻辑。

ThinkPHP 的请求是同步阻塞的,不加干预时根本扛不住并发
ThinkPHP 默认使用 PHP-FPM 的同步阻塞模型,每个请求独占一个进程/线程。100 个并发进来,就得有 100 个可用 worker,否则直接排队或 503。这不是 ThinkPHP 特有,但它的中间件、数据库连接、日志写入等默认行为会放大阻塞效应。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 确认
fpm.conf中pm.max_children设置合理(比如 32–64),别盲目设成 200,内存会爆 - 禁用
debug模式:生产环境必须关掉app_debug = false,否则日志和 trace 会严重拖慢响应 - 避免在控制器里做
sleep()、file_get_contents()这类同步 I/O,ThinkPHP 不支持原生协程 - 检查是否误启了
session_start()—— 默认 Session 文件驱动会锁文件,高并发下互相等待
数据库连接池不存在,但可以减少连接争抢
ThinkPHP 自带的 PDO 连接是“按需创建 + 长连接复用”,但它没有连接池机制,也没有自动熔断或降级。大量请求同时查库,MySQL 的 max_connections 很快被耗尽,出现 SQLSTATE[HY000] [1040] Too many connections。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在数据库配置中启用
'deploy' => 0(禁用读写分离),避免额外连接开销;真要读写分离,用中间件控制,别全量走 - 强制复用连接:配置
'params' => [\PDO::ATTR_PERSISTENT => true],但注意 MySQL 服务端要调大wait_timeout - 查询前加
Db::connect('name')->query('SELECT 1')主动探测连接有效性,避免拿到底层已断的连接 - 慎用
Db::transaction(),事务期间连接一直被占着,超时或异常未释放会导致连接泄漏
缓存不是万能的,Redis 用错反而更慢
很多人一提并发就加 Redis 缓存,但 ThinkPHP 的 Cache::get() 默认用的是文件驱动,高并发下文件锁会让所有请求排队等一个 cache 文件。切到 Redis 后,又常因 key 设计不合理或没设过期时间,导致击穿、雪崩或内存溢出。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 立刻把缓存驱动切到 Redis:
'type' => 'redis',并确认redis.conf中maxmemory-policy设为allkeys-lru - 缓存 key 必须带业务前缀和版本号,比如
user:profile:v2:12345,避免改结构后旧缓存污染新逻辑 - 绝不缓存空结果(
null或[])而不设短过期(如 60 秒),否则缓存穿透风险极高 - 批量操作别用
Cache::set($key, $val)循环,改用Redis::handler()->mset($data)原生命令
队列不是可选项,是并发场景下的事实标准
用户下单、发短信、写日志这类非实时强依赖的操作,硬顶在 HTTP 请求里处理,等于主动把并发压力拉满。ThinkPHP 的 think-queue 插件只是个封装,底层还是依赖 Redis 或数据库,配置不当一样卡死。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 消息体尽量小:只传 ID(如
order_id),不要传整个订单数组,序列化+反序列化成本高 - 消费者进程必须用
php think queue:work --daemon启动,禁用--once,否则每条消息都 reload 应用,CPU 直接拉满 - Redis 队列记得设
expire:在config/queue.php里给redis.options.queue加上'expire' => 3600 - 失败任务别堆在
failed_jobs表里不管,定期跑php think queue:flush清理,否则表越来越大,查询变慢
真正难的不是加 Redis 或上队列,而是判断哪部分该异步、哪部分必须同步返回、以及异步失败后怎么兜底——这些没法靠框架自动解决,得一行行看业务逻辑。



















