PHP 各框架下和 Go 的性能比较

[复制链接]
发表于 2026-9-10 15:57:25 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
在制作 AI GO ADMIN 的接口节流功能的时候,需要跨平台进行测试,突发奇想,何不找个全新的线上服务器,压测一下常见 PHP 框架和 Go 的来个对比,看看性能差距到底如何。
ai-go-admin 是一款主打可视化 CRUD 和极简内核的开源后台体系,完全诞生于 AI 时代,技术栈是 Golang + VUE3,最近刚做完,完全开源免费商用,欢迎体验:https://github.com/ai-go-hub/ai-go-admin
测试环境



  • CPU:4核 AMD EPYC 9K65 192-Core Processor
  • 内存:8G
  • 硬盘:通用 SSD(50 GB)
  • 操作体系:Ubuntu Server 26.04 LTS 64位
  • 基础资源占用:CPU 约 1%,内存约 12%
测试代码

代码直接让 AI 写的,目检了一下没啥大问题,放在文末。
测试代码规划纯凭感觉,一是需要含数据库业务,同一使用 PostgreSQL 数据库,同一个表,其中预设了 10 行数据,测试程序需要查询所有数据,然后直接输出数据条数。
人工确定了查库的逻辑是存在(生性多疑的我怕 AI 直接给我输出 10 😂),然后是支持常驻内存的,需要对数据库连接句柄进行维护,而不是每次都重新连接。
不返回所有数据内容,只是单纯的返回数据条数;不返回 JSON,各人都直接返回纯文本即可。
所有框架都没有引入多余依赖,做最简实现。
  框架/语言 版本 说明     Go 1.27.0 net/http、pgx 连接池   php-fpm 8.2 使用了 PHP 自带的 pgsql 扩展   thinkphp 8.0 think-orm   webman 2.1 使用了 PHP 自带的 pgsql 扩展   ai go admin 1.26.6 这份是功能齐全的完整体系,关闭节流后进行测试,全套生产级依赖,含:Gin、GORM 等  测试流程

首先将 pgsql 服务性能开到最高,最大连接数搞到最大,再对以上五份代码逐一、单独测试。
开始测试前总是确保服务器处于空闲状态,然后直接压测,确定已经将服务器资源全部占满且压测无任何报错为止。
测试结果

  框架/语言 RPS     原生 PHP 1363.16   Thinkphp 142.88   Webman 58084.70   原生 Go 51993.83  首先是 ThinkPHP 属实是拉完了,PHP-FPM 也不太给力。
然后 PHP 这边的大亮点是 Webman,它比原生 Go 还多点,从另外的角度证实 PHP 确实另有一战之力;
另外值得表明一下的是:Webman 基于多进程模子,单个请求发生 IO 阻塞时会占用整个工作进程,导致该进程上的全部请求被阻塞(以上压测体现不出来);而 Go 的 Goroutine 调度可以做到 M:N 调度隔离,单个请求阻塞不会影响其他请求。同时 Go 的内存管理心智负担小得多,所以并不是 Go 拉胯,应该是有所取舍?
根据 workerman 官方的数据,使用 ORM 的话每秒请求数大概要降一半,也就是大概 2.9W,Go 加上 Gin + GORM + 生产全套依赖 后另有 3.3W 左右(见后续 AI GO ADMIN 压测),个人感觉综合下来两者并发性能其实大差不差,实在要对比语言的话还是得看其他方面的差异,不再本文讨论范围内。
以下依次列出了具体压测数据
原生 PHP

  1. wrk -t4 -c100 -d30s http://127.0.0.1:8081/index.php
  2. Running 30s test @ http://127.0.0.1:8081/index.php
  3.   4 threads and 100 connections
  4.   Thread Stats   Avg      Stdev     Max   +/- Stdev
  5.     Latency    73.84ms   21.63ms 427.87ms   84.75%
  6.     Req/Sec   342.38     37.92   480.00     70.83%
  7.   40941 requests in 30.03s, 7.77MB read
  8. Requests/sec:   1363.16
  9. Transfer/sec:    264.91KB
复制代码
Thinkphp

  1. wrk -t4 -c100 -d30s http://127.0.0.1:8082/bench
  2. Running 30s test @ http://127.0.0.1:8082/bench
  3.   4 threads and 100 connections
  4.   Thread Stats   Avg      Stdev     Max   +/- Stdev
  5.     Latency   691.23ms  121.58ms   1.27s    71.48%
  6.     Req/Sec    36.56     18.96   121.00     70.87%
  7.   4291 requests in 30.03s, 838.09KB read
  8. Requests/sec:    142.88
  9. Transfer/sec:     27.91KB
复制代码
Webman

  1. wrk -t4 -c100 -d30s http://127.0.0.1:8787/bench
  2. Running 30s test @ http://127.0.0.1:8787/bench
  3.   4 threads and 100 connections
  4.   Thread Stats   Avg      Stdev     Max   +/- Stdev
  5.     Latency     2.20ms    3.33ms 129.03ms   94.51%
  6.     Req/Sec    14.63k     7.28k   56.68k    62.20%
  7.   1748345 requests in 30.10s, 175.07MB read
  8. Requests/sec:  58084.70
  9. Transfer/sec:      5.82MB
复制代码
原生 Go

  1. wrk -t4 -c100 -d30s http://127.0.0.1:8090/bench
  2. Running 30s test @ http://127.0.0.1:8090/bench
  3.   4 threads and 100 connections
  4.   Thread Stats   Avg      Stdev     Max   +/- Stdev
  5.     Latency     2.26ms    3.20ms  77.06ms   97.91%
  6.     Req/Sec    13.07k     1.81k   16.08k    92.67%
  7.   1560300 requests in 30.01s, 175.59MB read
  8. Requests/sec:  51993.83
  9. Transfer/sec:      5.85MB
复制代码
ai-go-admin 压测

首先得关闭接口节流(config/throttle.yaml),然后调高数据库连接池配置(config/config.yaml 里边的 max_open_conns: 200 和 max_idle_conns: 100)。
先做了个不带数据库业务的

  1. wrk -t4 -c100 -d30s http://127.0.0.1:8080/ping
  2. Running 30s test @ http://127.0.0.1:8080/ping
  3.   4 threads and 100 connections
  4.   Thread Stats   Avg      Stdev     Max   +/- Stdev
  5.     Latency     1.05ms    1.36ms  28.50ms   88.09%
  6.     Req/Sec    36.98k     3.51k   50.69k    72.75%
  7.   4416345 requests in 30.01s, 690.73MB read
  8. Requests/sec: 147154.69
  9. Transfer/sec:     23.02MB
复制代码
带数据库业务

  1. wrk -t4 -c100 -d30s http://127.0.0.1:8080/bench
  2. Running 30s test @ http://127.0.0.1:8080/bench
  3.   4 threads and 100 connections
  4.   Thread Stats   Avg      Stdev     Max   +/- Stdev
  5.     Latency     3.33ms    2.79ms  57.06ms   78.72%
  6.     Req/Sec     8.47k   675.08    10.81k    75.58%
  7.   1010958 requests in 30.00s, 176.43MB read
  8. Requests/sec:  33694.62
  9. Transfer/sec:      5.88MB
复制代码
测试代码

pgsql 初始化

  1. -- 测试表:5 个字段
  2. CREATE TABLE test_users (
  3.     id         SERIAL PRIMARY KEY,
  4.     username   VARCHAR(64)  NOT NULL,
  5.     email      VARCHAR(128) NOT NULL,
  6.     age        INT          NOT NULL,
  7.     created_at TIMESTAMP    NOT NULL DEFAULT NOW()
  8. );
  9. -- 插入 10 行测试数据
  10. INSERT INTO test_users (username, email, age, created_at) VALUES
  11.     ('alice01',  '[email protected]',  21, '2026-01-01 09:00:00'),
  12.     ('bob02',    '[email protected]',    22, '2026-01-02 09:00:00'),
  13.     ('carol03',  '[email protected]',  23, '2026-01-03 09:00:00'),
  14.     ('dave04',   '[email protected]',   24, '2026-01-04 09:00:00'),
  15.     ('eve05',    '[email protected]',    25, '2026-01-05 09:00:00'),
  16.     ('frank06',  '[email protected]',  26, '2026-01-06 09:00:00'),
  17.     ('grace07',  '[email protected]',  27, '2026-01-07 09:00:00'),
  18.     ('henry08',  '[email protected]',  28, '2026-01-08 09:00:00'),
  19.     ('iris09',   '[email protected]',   29, '2026-01-09 09:00:00'),
  20.     ('jack10',   '[email protected]',   30, '2026-01-10 09:00:00');
复制代码
Golang 原生

  1. // Go 基准测试程序:net/http + pgx 连接池
  2. // 每次请求:从连接池取连接 -> 查询 test_users 全表 -> 返回行数
  3. package main
  4. import (
  5.         "context"
  6.         "fmt"
  7.         "log"
  8.         "net/http"
  9.         "os"
  10.         "strconv"
  11.         "github.com/jackc/pgx/v5/pgxpool"
  12. )
  13. // 数据库配置(硬编码,与其它测试程序保持一致)
  14. const (
  15.         dbHost     = "127.0.0.1"
  16.         dbPort     = 5432
  17.         dbName     = "phpgo_bench"
  18.         dbUser     = "bench"
  19.         dbPassword = "postgres"
  20.         // 连接池大小:默认等于 GOMAXPROCS,压测时可按 CPU 核数调整
  21.         poolMaxConns = 8
  22.         listenAddr = ":8090"
  23. )
  24. var pool *pgxpool.Pool
  25. func benchHandler(w http.ResponseWriter, r *http.Request) {
  26.         var n int
  27.         err := pool.QueryRow(r.Context(),
  28.                 "SELECT count(*) FROM (SELECT id, username, email, age, created_at FROM test_users) t",
  29.         ).Scan(&n)
  30.         if err != nil {
  31.                 http.Error(w, "query failed: "+err.Error(), http.StatusInternalServerError)
  32.                 return
  33.         }
  34.         w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  35.         fmt.Fprint(w, strconv.Itoa(n))
  36. }
  37. func main() {
  38.         dsn := fmt.Sprintf("host=%s port=%d dbname=%s user=%s password=%s pool_max_conns=%d",
  39.                 dbHost, dbPort, dbName, dbUser, dbPassword, poolMaxConns)
  40.         cfg, err := pgxpool.ParseConfig(dsn)
  41.         if err != nil {
  42.                 log.Fatalf("parse dsn: %v", err)
  43.         }
  44.         pool, err = pgxpool.NewWithConfig(context.Background(), cfg)
  45.         if err != nil {
  46.                 log.Fatalf("create pool: %v", err)
  47.         }
  48.         if err := pool.Ping(context.Background()); err != nil {
  49.                 log.Fatalf("ping db: %v", err)
  50.         }
  51.         mux := http.NewServeMux()
  52.         mux.HandleFunc("/bench", benchHandler)
  53.         mux.HandleFunc("/", benchHandler)
  54.         fmt.Printf("go bench listening on %s\n", listenAddr)
  55.         if err := http.ListenAndServe(listenAddr, mux); err != nil {
  56.                 log.Fatalf("listen: %v", err)
  57.                 os.Exit(1)
  58.         }
  59. }
复制代码
php-fpm

[code]
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表