laravel本身不是瓶颈,高并发下垮掉的是数据库连接池耗尽、缓存击穿、会话阻塞、同步任务堆积;可扩展性靠分层解耦+异步化+无状态化。

直接说结论:Laravel 本身不是瓶颈,高并发下弹性伸缩的关键在于「无状态化 + 分层解耦 + 缓存前置 + 异步兜底」。硬堆 PHP-FPM 实例或盲目上 Octane 并不能解决问题,反而可能掩盖真实瓶颈。
为什么 Laravel 应用在高并发时容易卡住?
常见错误现象是 Nginx 返回 502 Bad Gateway 或 504 Gateway Timeout,日志里反复出现 upstream timed out。这不是 Laravel 写得烂,而是架构没做无状态分离:
- 会话(session)还存在本地文件或数据库中,导致负载均衡后用户请求被分到不同机器却找不到 session
- 缓存用的是
file驱动,多实例间缓存不共享,击穿率飙升 - 所有服务(API、队列、定时任务)混跑在同一组 FPM 进程里,一个慢查询拖垮整台机器
- 模型里写死
DB::connection('tenant_1'),扩容时无法自动适配新租户库
必须把 Laravel 拆成「三类无状态进程」
弹性伸缩的前提是每个实例可随时启停,不依赖本地状态。按职责拆分:
-
HTTP 入口进程:只处理鉴权、限流、路由转发,不做业务逻辑。推荐用
Laravel Octane(Swoole/RoadRunner),禁用php-fpm;若仍用 FPM,至少为网关单独配子域名(如api.example.com)并调大fastcgi_read_timeout -
异步工作进程:专跑
queue:work,驱动必须是redis,禁用sync和database;每个业务域用独立队列名,比如user-events、payment-jobs -
定时任务进程:用
supervisor单独管理scheduler:run,避免和 HTTP 进程争资源;关键任务加->onQueue('critical')并配 Horizon 优先级
缓存层怎么配才扛得住百万 QPS?
别只改 CACHE_DRIVER=redis 就完事。缓存策略直接影响伸缩效果:
-
redis驱动必须开启cache_tags支持(Laravel 默认关),否则Cache::tags(['users'])->flush()会失效 - 高频读场景(如用户权限)用
Laravel Permission的内置缓存,但要重写PermissionRegistrar的getPermissions方法,加一层apcu本地缓存,避免每次查 Redis - 不要对 Eloquent 模型直接
Cache::remember,而应缓存 DTO 或数组;模型带关系、访问器、序列化逻辑,反序列化开销大 - 配置里显式关闭
config('cache.stores.redis.prefix')的默认前缀,避免 Redis key 冲突影响横向扩容
数据库层不拆分,再好的 PHP 架构也白搭
单库扛不住 2000+ TPS,分库分表不是“将来再做”,而是上线前就该定好分片键:
- 绝对不用自增 ID 或时间戳当分片键——写入热点集中在最新分片,其他分片闲置
- 用户维度业务,用
hash(user_id) % 8做初始分片;订单维度,用hash(order_no) % 16,保证分布均匀 - 分库后,Eloquent 模型不能靠
DB::connection('shard_1')切库,而要用「连接绑定」:在User模型里加protected $connection = 'user_shard';,让迁移、工厂、关系都自动走对应连接 - 跨分片 JOIN 是禁区,查用户+订单列表必须走应用层聚合,或提前用消息队列同步宽表到 Elasticsearch
最常被忽略的一点:弹性伸缩不是“加机器就完事”,而是每次扩容都要验证「缓存穿透是否可控」「队列延迟是否上涨」「分片键倾斜度是否恶化」。这些指标比 CPU 使用率更能说明系统是否真能弹性起来。











