
PHP 各框架下和 Go 的性能比较这个话题我在技术群里见过太多次了。每次一有人抛出来评论区基本就会分成两派一边说 PHP 该淘汰了一边说业务跑得好好的换什么换。而绝大多数争论都停留在口号层面没有人把具体哪个框架、哪种业务场景、什么压测条件讲清楚所以谁也说服不了谁。我自己是两种技术栈都在用的状态PHP 写了十几年线上一直有 Laravel 和 ThinkPHP 项目在维护最近几年把一部分高并发管道业务切到了 Go。这篇文章不是来当裁判的而是想把我在同一套环境下跑过的性能测试、遇到过的瓶颈、以及在几个真实业务场景里的选型思考完整记录下来。如果你正在纠结 Laravel 还是 Gin、Hyperf 能不能顶替 Go或者单纯想弄明白PHP 慢到底慢在哪这篇应该能给你一个相对完整的参考。1. 先泼盆冷水PHP慢、Go快这句口诀经不起细问1.1 同样叫PHP性能上下能差几十倍很多人说PHP 慢但实际指的是PHP-FPM 跑传统 MVC 框架这个特定组合。同样是 PHPLaravel 和 Hyperf 在同一个接口上的 QPS 差距可能达到十倍甚至更多。Laravel 慢不代表 PHP 语言本身慢Hyperf 就是 PHP 写出来的应用框架但它采用了 Swoole 常驻内存和协程模型性能表现完全是另一个量级。反过来Go 也有写得很烂的代码GORM 全表查询加循环 N1调优之前照样能把服务拖垮。跨语言谈性能必须先明白一个前提框架、运行模式、代码写法的影响往往比语言本身更大。我见过最典型的例子是把 Laravel 的 Eloquent 用在循环里逐条读写200 行代码把接口拖到 5 秒以上换成批量查询加原生 SQL 后响应时间直接降到 80ms全程没有换语言。1.2 语言执行只是端到端耗时的一小块我见过很多团队把PHP 慢挂在嘴边可真正把慢接口拉出来看时发现 80% 以上的时间花在数据库 SQL、Redis 网络往返、第三方 API 调用上语言自身的执行时间可能只有百分之几。一个接口总耗时 200ms其中 PHP 脚本真正执行计算的时间可能只有 2ms剩下的基本都在等 IO。等 IO 这件事Go 的协程确实比 PHP-FPM 的进程模型更擅长但如果你只是想把 200ms 降到 195ms从语言层面下手是性价比很低的方案。先把慢 SQL 修掉把 N1 查询消灭掉把没必要的远程调用缓存起来这些优化带来的收益要明显得多。这也是为什么很多换 Go 之后性能飞升的故事细问下来其实是重写的时候顺手把烂 SQL 优化掉了而不是 Go 本身有多神。2. 同场竞技PHP 主流框架与 Go 主流框架的实测数据2.1 测试环境与压测口径先说明一下我的测试环境4 核 8G 云主机PHP 8.2 开了 OpCacheGo 1.22都用 Docker 部署在同一台机器上避免网络差异。压测工具用的 wrk固定 200 并发、持续 60 秒。为了尽量公平PHP 侧 Laravel 11、ThinkPHP 8、Symfony 7 都保持默认配置不做过度的性能裁剪Hyperf 3 按官方默认配置用 Swoole 运行Go 侧选了 Gin、Echo、标准库 net/http 三组。测试接口分三种纯 JSON 响应、单表 MySQL 查询后返回 JSON、Redis 读取后返回 JSON。这里必须强调这些数字是我这台机器上的相对趋势不是框架官方标准。换到不同硬件、不同 PHP/Go 版本、不同依赖环境下数字一定会有波动但相互之间的量级关系有参考价值。另外PHP 侧默认没有开 JIT开了 JIT 对纯 CPU 计算有帮助但对 Web 请求中占比很大的框架启动和 IO 等待提升并不明显。2.2 纯 JSON 接口框架厚度立刻拉开差距最简单的纯 JSON 接口最能反映框架本身的启动成本。实测下来Laravel 11 大致在 700 到 900 QPS 之间ThinkPHP 8 能到 1500 QPS 左右Symfony 7 在 900 到 1100 QPS 之间到了常驻内存的 Hyperf 3能跑到 8000 QPS 以上。Go 这边Gin 在 2 万 QPS 上下Echo 略低一点标准库 net/http 大概 2.5 万到 3 万 QPS。Laravel 和 Gin 之间接近三十倍的差距看起来吓人但拆开看主要来自两件事一是 PHP-FPM 每请求都重建环境二是 Laravel 本身就是厚框架服务容器、门面、中间件管道、事件系统在每次请求里都要完整走一遍。Gin 的路由用压缩前缀树中间件只是链式函数调用JSON 序列化走标准库启动成本被压到了极低。2.3 加入 MySQL 和 Redis 后差距快速收窄为了贴近真实业务第二个接口做了单表查询并按 id 返回一条用户记录。这时候差异非常有意思Laravel 降到 300 左右ThinkPHP 400 多Symfony 350 上下Hyperf 能到 1500 上下Gin GORM 大约 1800 到 2200 QPS。数据库访问成了新的瓶颈PHP 和 Go 的差距从三十倍缩小到六倍以内。第三个接口读 Redis 再返回 JSONLaravel 在 600 左右Hyperf 能到 3500 左右Gin 大概 6000 到 8000。核心原因很简单当请求时间里有大量等待交给数据库、Redis 时语言执行能力对整体 QPS 的影响被稀释了IO 等待和连接池吞吐才是大头。2.4 关键指标速览测试项Laravel 11ThinkPHP 8Symfony 7Hyperf 3Go Gin纯 JSON约 800约 1500约 1000约 8000约 20000MySQL 单查 JSON约 300约 450约 350约 1500约 2000Redis 读取 JSON约 600约 1000约 700约 3500约 7000看这张表能得出几个实际有用的结论如果你的系统绝大多数接口都要查数据库、调缓存那么 Laravel 和 Gin 的 QPS 差距没有传说中那么吓人如果你的系统有一些纯计算、纯转发的接口PHP-FPM 的劣势会暴露得非常明显。这也是为什么网关、短链服务、推送服务这类偏薄的场景Go 几乎是无脑选择。3. 差异的根子请求生命周期、并发模型与框架厚度3.1 PHP-FPM 的用完即焚模型PHP-FPM 下每个请求基本是无状态的Web 服务器收到请求FPM 分配一个进程或复用已有进程PHP 从头开始加载框架代码、注册服务、解析路由、执行控制器响应完之后把进程里的大部分资源释放掉。相当于每次来一桌客人都要把餐厅拆了重新装修一遍。框架本身越大启动开销越明显这就是 Laravel 在纯 JSON 接口上 QPS 上不去的最直接原因。OpCache 能缓存编译后的字节码但类加载、服务注册、对象构造这些开销依然每次请求都要发生。这也是 PHP 从设计上就和常驻型服务有差距的地方但注意这个差距属于请求模型不等于PHP 语言不行。PHP 官方这些年一直在推动 JIT但 Web 场景下收益有限因为瓶颈不在纯计算而是每次请求的生命周期太短还没来得及把热点代码跑热请求就结束了。3.2 Go goroutine 的并发模型Go 的并发模型是另一个方向进程启动后把所有代码加载进内存每个请求只需要创建一个 goroutine。goroutine 初始栈很小几 KB 起步一个进程轻松承载上万个并发连接配合 epoll 网络轮询读写请求大多数时候在等待网络数据时并不占用线程。用大白话说Go 的服务是一个人同时接待很多客人而 PHP-FPM 是一个服务员只服务一桌客人前者在并发场景下当然更省资源、吞吐更高。这是 Go 在高并发网关和推送场景优势的本质来源也是 PHP 社区里 Swoole、RoadRunner 这些项目拼命想补齐的能力。RoadRunner 的做法更有意思它本身是 Go 写的应用服务器PHP 只负责处理业务逻辑请求生命周期被 RoadRunner 接管也能显著缩小和 Go 的距离。3.3 框架厚度Laravel 为什么重Gin 为什么薄同样是 Go 框架Gin 之所以快除了语言本身还因为它足够薄路由用的是压缩前缀树中间件只是链式函数调用JSON 序列化走标准库。Laravel 强大是因为它把开发者需要的东西都塞进了请求生命周期里服务容器、门面、Eloquent ORM、事件系统、队列、认证、授权等每一样都有成本。ThinkPHP 比 Laravel 轻一个量级因为它的服务注册和对象管理更简单纯 JSON 接口 QPS 高出快一倍也印证了这一点。所以框架选型本质上是在启动开销和开发生产力之间做交换没有一个框架在所有维度上都占便宜。你在 Laravel 里享受的每一个省心的语法糖都在为接口延迟买单只不过大多数业务系统根本感知不到这几毫秒的差别。3.4 常驻内存 PHPSwoole/Hyperf 如何缩小差距Swoole 让 PHP 也能常驻内存配合协程实现类似 Go 的并发模型。Hyperf 就是基于 Swoole 构建的全栈框架代码常驻、对象复用、数据库连接池、协程调度所以它的纯 JSON 接口能跑到 8000 QPS已经和低配 Go 服务处于同一个数量级。代价是引入了一层需要专门运维的常驻进程服务代码里要时刻注意内存泄漏、协程安全、连接复用开发心智成本比传统 PHP-FPM 高不少。我用 Hyperf 做过消息推送服务稳定性没有问题但团队里如果有人依然拿 PHP-FPM 的思路写 Hyperf很快会踩到内存泄漏的坑。想用 PHP 追平 Go 的并发能力Swoole 系确实是唯一能打的路只是这条路的复杂度并不会比直接学 Go 低多少选哪条得看团队底子。4. 用 xhprof 和 pprof 把慢定位到函数级4.1 PHP 侧从 xhprof 到 Tideways看框架启动开销如果只是嘴上说Laravel 慢很多人是不服的所以我建议任何优化都先做函数级性能分析。老牌的 xhprof 在 PHP 7 之后兼容性一般我这边更喜欢用 tideways_xhprof它是 xhprof 的现代分支基本支持 PHP 8。安装方式也很简单pecl install tideways_xhprof在 php.ini 里加上 extensiontideways_xhprof.so然后在你想要分析的入口开启\Tideways\Profiler::start([ collect_memory true, collect_malloc true, ]); // 业务代码... \Tideways\Profiler::stop();跑完能输出火焰图和函数耗时列表。我在 Laravel 项目上做过一次分析结果非常典型一个简单的控制器真正执行业务逻辑的时间只占整个请求的三成左右剩下的大部分都耗在 Composer 自动加载、ServiceProvider 注册、门面解析、路由匹配和中间件管道上。这就是框架厚度的直接证据。4.2 Go 侧go tool pprof 的使用逻辑Go 这边的性能分析工具链成熟很多。要在 Web 服务里打开 pprof只需要引入标准库的接口import _ net/http/pprof如果服务框架接管了默认端口可以单独起一个 goroutine 对外暴露 pprof 端口go func() { http.ListenAndServe(:6060, nil) }()要采集 CPU 30 秒的 profile命令是这样的go tool pprof http://localhost:6060/debug/pprof/profile?seconds30进入交互界面后输入 top 能看到耗时最高的函数输入 web 能生成火焰图并在浏览器打开。常见瓶颈也很套路化JSON 序列化、正则匹配、反射调用、数据库连接等待。有一点要注意线上开 pprof 会对性能有一点影响一般建议只在压测机或低流量时段开短时间的 profile不要让它常驻生产。4.3 案例同一个 Redis 消费组PHP 和 Go 的差距在哪我拿 Redis 消费组场景做个实测案例这也是很多团队从 PHP 迁 Go 的第一个点。PHP 侧我写了一个 CLI 常驻脚本用 XREADGROUP 阻塞读取新消息逐条处理Go 侧用同样的逻辑但开了 20 个 worker goroutine 并发处理外层用 channel 分发。测试数据是本机 Redis 里的 10 万条 JSON 消息处理内容只是解析 JSON、累加几个字段再写回结果集。PHP 单进程大概跑到每秒 3000 到 5000 条Go 跑到了每秒 4 万条以上。这个差距来自两层一是 Go 的并发拆分了 IO 等待Redis 往返延迟被多个 worker 分摊二是同等逻辑下Go 处理 JSON 解析的 CPU 效率也明显更高。如果你现在也是把 PHP 当队列消费者用换成 Go 或者至少换成 Swoole 常驻进程收益都非常明显。这也是热词里php redis 消费组背后最常见的真实需求——不是 PHP 做不了而是它用进程模型做这件事成本太高。5. 场景决定选型别让基准测试骗了你5.1 CPU 密集场景图片处理、视频压缩Go 有优势但不万能热词里能看到不少人搜 PHP 图片生产、PHP 视频压缩。PHP 用 GD、Imagick 做图片处理简单场景没问题但到了批量缩略图、视频抽帧、压缩转码这类 CPU 密集型任务PHP 的执行效率明显不如 Go。Go 有 imaging、ffmpeg-go 这类库性能表现好不少。但说句实话真正的重计算——批量视频转码、大量图像滤镜叠加——无论 PHP 还是 Go都不如 C/C/Rust 或者直接调 ffmpeg 命令行。比较合理的设计是业务系统用 PHP 接收任务、管理队列把真正的计算放到独立 Worker 里Worker 可以用 Go 写也可以直接调系统级的 ffmpeg 进程。性能优化不一定是换语言的动机把计算从 Web 请求链路里拆出去往往比换语言更便宜、更见效。5.2 IO 密集与高并发网关、推送、长连接Go 几乎是无脑选择WebSocket 长连接、推送网关、API 网关这类 IO 密集型服务对 PHP-FPM 来说是结构性的不匹配。FPM 一个进程同时只能服务一个请求长连接会把进程全部占满Swoole 能靠协程扛几万连接Go 的 goroutine 模型能更轻松地扛到几十万连接级别。同样是做秒级推送我见过用 Go 写的网关单机支撑几十万在线连接这在 PHP-FPM 下基本是不可想象的。如果你的核心业务场景是这些别犹豫用 Go。但要注意Go 的高并发是有代价的goroutine 泄漏、channel 阻塞、内存增长这类问题排查起来比 PHP 的请求结束就释放要复杂得多团队没有相应基础上线后运维成本会直接拉高。这也是为什么有些团队迁到 Go 之后反而觉得比 PHP 更难维护性能上去了人的压力也上去了。5.3 复杂业务后台为什么 PHP 依旧能打很多后台系统、CRM、运营管理平台、中后台电商管理核心瓶颈根本不是 QPS而是业务复杂度、权限模型、工作流状态机、报表 SQL 这种开发效率和灵活性问题。这类系统的 QPS 需求可能只要几百但功能清单长到吓人。Laravel 的 Eloquent、队列、事件、认证授权、社区生态包能让一个中等水平的团队在很短的时间内搭建出功能丰满的系统。我用 Go 写过类似的复杂后台最后的感受是业务逻辑本身不复杂但数据模型映射、枚举状态流转、动态查询条件拼装这些在 PHP/Java 里很顺手的事在 Go 里写得又长又容易出错。所以在这个赛道里PHP 慢根本不是一个有效的反对理由因为系统的瓶颈从来不在语言上。你要是硬把一个需求复杂、迭代频繁的后台重写成 Go大概率会得到一套性能没有本质提升、但开发速度明显变慢的系统。6. 从 PHP 转 Go 的隐藏成本与混合架构经验6.1 隐藏成本不只是重写代码那么简单性能不是你迁移技术栈的唯一成本。团队学习曲线是最容易被低估的一项PHP 里一切皆数组的开发体验转到 Go 之后会立刻感受到静态类型和手动错误处理的繁琐。拿最常见的数据结构来说PHP 一个关联数组能装下所有东西Go 里你得定义一个 struct还要考虑它是否实现了某个接口。再加上 composer 生态里大量成熟的库在 Go 里要么没有、要么需要自己封装比如支付、物流、ERP 对接这类业务包PHP 社区基本开箱即用Go 社区则常常需要自己花时间补齐。如果只是为了性能而全面重写一个功能复杂的业务系统大概率会在重写过程中发现性能根本不是主要矛盾反而把业务开发节奏拖垮了。我见过不止一个团队用三个月重写旧系统半年后还在一遍遍对功能上线时间一拖再拖。6.2 什么业务值得迁什么业务留着我自己的判断标准很简单先看系统的性能瓶颈是不是出在语言执行层面。如果瓶颈在数据库慢查询、业务逻辑复杂度和功能迭代速度上换语言解决不了问题如果瓶颈是单机高并发转发、海量长连接、CPU 密集计算而且这些场景可以独立成服务那就有充分的理由用 Go 从零构建而不是把老业务全部推倒。换句话说做增量迁移不做整体推翻。把新服务用 Go 写在系统边缘把老 PHP 业务留在中心是风险和收益平衡得最好的姿势。比如对接外部系统的网关、实时数据处理管道、WebSocket 推送服务这些边界清晰、依赖少的模块最适合先切过去而核心业务、后台管理、订单流程这类和数据库绑定深、逻辑联动多的部分留在 PHP 里反而是更好的选择。6.3 我的混合架构实践我现在的线上架构对外入口是 Nginx 加一层 Go 写的 API 网关负责统一鉴权、限流、灰度路由网关后面接了 PHP 的 Laravel 后台业务系统和 Go 的高并发任务管道两者通过 Redis 和消息队列通信。高并发、低延迟、纯转发的接口走到 Go复杂业务逻辑、后台管理、报表、工作流留在 PHP。跑了一年多这个架构的稳定性出乎意料地好PHP 团队不用学习 GoGo 组也不需要深入 PHP 业务细节两边通过接口契约协作冲突很少。对我来说技术选型的答案从来不是PHP 还是 Go而是这个流量和复杂度下哪个组合的方式最省事。性能数字只是决策依据里的一列不是全部。最后再说一个我自己养成的习惯任何技术选型之前先拿真实流量的一小部分做一次最小压测再让团队一起看看这段逻辑在两种技术栈下的可维护性最后再投票。语言之争背后真正决定项目成败的往往不是并发模型这个单一因子团队熟悉度、生态覆盖度、出问题时的排查效率这些东西不会出现在 QPS 数字里但会在每一次上线和故障处理时反复出现。这套方法论比记住我上面任何一个表格里的数字都更值得带走。