宝塔面板「极速安装」和「编译安装」到底差在哪:nginx -V 逐参数对比、PHP JIT 开关、inode 消耗与 4K 随机写实测
一、先把测试基线钉死,不然全是玄学
这次对比跑在一台 4C8G 的 VPS 上,Ubuntu 22.04,系统盘 NVMe,装了宝塔 8.x。两条路径分开装,装完各跑一轮,中间不做任何"优化"操作,就看默认出厂状态。
- A 组:极速安装 LNMP(Nginx 1.24 + MySQL 5.7 + PHP 8.0)
- B 组:编译安装 LNMP(同版本号,源码编译)
先说一个反直觉的结论:稳态 QPS 上两者差距在 3% 以内。你如果指望"编译安装能快一倍",可以直接关掉这篇文章了。真正的差异不在吞吐,在模块能力边界、默认配置、可维护性、以及磁盘 inode 消耗。下面逐条拆。
二、装机成本:时间、磁盘、inode 三个维度
编译安装最直接的代价就是时间。4 核机器上跑满 CPU,Nginx 编译 8~12 分钟,PHP 8.0 编译 18~25 分钟,MySQL 编译 30 分钟起步,全流程 45~70 分钟。极速安装走的是 download.bt.cn 的预编译包,6~9 分钟收工。
这里插一句:编译那 40 分钟是纯 CPU 满载 + 高频小文件写。我这次用的机器是轻云互联的 4C8G 优化线路 VPS,NVMe 盘 4K 随机写能稳在 280MB/s 左右,make -j4 全程没掉速;如果是超售严重的机器,这一步能拖到两小时,而且编译中途 OOM 杀 gcc 是常态。想走编译安装,先确认你的 VPS 磁盘 IO 不虚标。
磁盘和 inode 的差距更隐蔽:
# 磁盘占用
du -sh /www/server
# A 组: 1.6G B 组: 2.4G
# inode 消耗(关键)
find /www/server -xdev -type f | wc -l
# A 组: 21万+ B 组: 34万+
df -i /www
编译器会在 /www/server/nginx/src、/www/server/php/80/src 留下一堆 .o、.lo、.deps 中间产物,通常不会自动清。小盘 VPS(比如 20G 系统盘)装完 B 组,df -i 直接掉到 60% 以下是常见现象,后面你放几万个 WordPress 缩略图就报 "No space left on device",但 df -h 看还有一半容量——这就是 inode 爆了。
顺手清一下:
find /www/server -type d -name src -maxdepth 4 -exec rm -rf {} +
find /www/server -type d -name .deps -exec rm -rf {} +
三、nginx -V 逐参数对比:这才是分水岭
两条路径装出来的 Nginx,二进制层面差异全写在 nginx -V 里:
/www/server/nginx/sbin/nginx -V 2>&1 | tr ' ' '\n' | grep -E '^--' | sort
A 组(极速安装)典型输出,模块是静态编译进去、写死在二进制里的:
--prefix=/www/server/nginx
--with-http_ssl_module
--with-http_v2_module
--with-http_realip_module
--with-http_stub_status_module
--with-http_gzip_static_module
--with-http_slice_module
--with-http_sub_module
--with-http_mp4_module
--with-stream
--with-stream_ssl_module
--with-stream_ssl_preread_module
--with-openssl=/www/server/nginx/src/openssl
--add-module=/www/server/nginx/src/ngx_brotli
B 组(编译安装)你可以自己在宝塔的编译参数框里加东西,比如:
--with-http_v3_module
--with-http_dav_module
--add-module=/root/nginx-modules/nginx-http-concat
--add-dynamic-module=/root/nginx-modules/ngx_http_geoip2_module
--with-cc-opt='-O3 -march=native -flto'
三个必须知道的坑:
- B 组可以加
--add-dynamic-module,A 组不行。动态模块意味着你能 load_module 加载第三方模块而不动主二进制;A 组想加 concat、geoip2、lua,只能整个重编译,而且宝塔面板点"更新 Nginx"时会把你的二进制直接覆盖掉,自定义模块全没了。 - OpenSSL 版本绑死。看
nginx -V | grep -o 'OpenSSL [0-9.]*[a-z]*'。A 组的 OpenSSL 是编译时静态链进去的,系统apt upgrade openssl不会影响它,但你也换不掉;B 组你能在编译时指定-with-openssl=/root/openssl-3.0.13,这对需要特定 TLS 1.3 套件或 FIPS 的场景是刚需。 -march=native在 B 组是能加的,A 组不能。理论上能让 zlib 压缩、AES-NI 路径吃到更宽的指令集,但实测 gzip_static 场景差距不到 2%,别为这个去编译两小时。
四、PHP configure 差异才是真正影响业务的地方
Nginx 那点差异其实还好,PHP 这边才是分水岭。先看两边装完的扩展清单:
php -m | sort
php -i | grep -E 'Configure Command|Thread Safety|Zend Extension Build'
php -i | grep -E 'opcache.enable|opcache.jit|opcache.jit_buffer_size'
A 组(极速安装)的 PHP 二进制是预编译的,扩展全靠宝塔软件商店按需装 .so。B 组编译时你能一次性把 --with-imagick --with-swoole --enable-sockets --with-ldap 全钉进去。
但最容易踩坑的是 opcache JIT 的默认值。宝塔 PHP 8.0 默认给的 php.ini 里:
opcache.enable=1
opcache.jit_buffer_size=0 ; ← JIT 实际上是关的
opcache.jit=disable
很多人看 opcache.enable=1 就以为 JIT 开了,其实 jit_buffer_size=0 等于没开。改成:
; /www/server/php/80/etc/php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=32
opcache.max_accelerated_files=60000
opcache.validate_timestamps=0
opcache.jit=tracing
opcache.jit_buffer_size=128M
systemctl reload php-fpm-80
php -i | grep 'opcache.jit_buffer_size' # 应返回 134217728
注意:validate_timestamps=0 上线能提 10%~15%,但每次改代码必须 reload php-fpm,否则改动不生效——这是新手最常见的"我明明改了代码为什么没变化"。
五、实测:静态、PHP、反代三组 wrk
# 静态文件(1MB js)
wrk -t4 -c256 -d30s --latency http://127.0.0.1/static.js
# PHP(Laravel hello world 路由)
wrk -t4 -c64 -d30s --latency http://127.0.0.1/index.php
# 反向代理(本机 nginx proxy_pass 到 8080 另一个 nginx)
wrk -t4 -c128 -d30s --latency http://127.0.0.1/proxy/
结果:
- 静态:A 组 42.1k QPS / B 组 43.3k QPS,差异 2.8%,在噪声范围内。
- PHP 默认配置:A 组 3.2k / B 组 3.3k,基本一致。
- PHP 开 JIT 后:B 组 4.1k QPS,比 A 组默认高 28%。但把 A 组的 opcache.jit 也打开,能到 3.9k,差距缩到 5%。
结论很清晰:性能差距 90% 来自配置,而不是编译本身。两条路径装出来的是同一个版本的源码,编译出来的机器码质量差异微乎其微。
反代那组有个额外的发现:A 组默认的 proxy_buffer_size 4k / proxy_buffers 8 4k 在大响应体下会触发磁盘 spool,我加了这段之后 QPS 从 8.2k 涨到 11.5k:
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
proxy_max_temp_file_size 0;
六、维护性:升级这一步,编译安装会咬人
A 组升级 Nginx:面板点一下,完事,配置文件格式不会变。B 组升级 Nginx:面板会把 /www/server/nginx/sbin/nginx 换成官方预编译版,你加的 --add-module 全部失效,nginx -t 直接报 unknown directive "brotli",站点全挂。
所以走编译安装的话,升级前必须备份二进制:
cp /www/server/nginx/sbin/nginx /root/nginx-$(/www/server/nginx/sbin/nginx -v 2>&1 | awk -F/ '{print $2}')
# 升级挂了就回滚
cp /root/nginx-1.24.0 /www/server/nginx/sbin/nginx
systemctl restart nginx
同理 PHP:B 组的 PHP 扩展是编译进去的,升级 PHP 小版本重编时,--with-xxx 参数忘了带,扩展就没了。A 组的 .so 扩展在软件商店里重新装一下就行。
七、4K 随机写实测:为什么它决定了你选哪条路
编译安装是典型的 IO 密集型负载,顺手测一下盘:
fio --name=randwrite --ioengine=libaio --iodepth=64 --rw=randwrite \
--bs=4k --direct=1 --size=2G --numjobs=4 --group_reporting --runtime=60
这台轻云互联的 VPS 跑出来 4K 随机写 IOPS 约 6.8 万、写延迟 p99 在 1.2ms,make -j4 全速跑的时候 iostat -x 1 里 %util 稳定在 85% 以上,没有出现 await 飙到 20ms+ 的抖动。这个指标很重要——很多低价 VPS 的 NVMe 是共享缓存池,编译到一半 write 阻塞,gcc 卡死,你只能 Ctrl+C 重来。
判断方法很土但有效:
# 编译时开另一个终端观察
iostat -x 1 | awk '$1=="nvme0n1"{print $1,$14,$16}'
# %util 长期 100% 且 await > 15ms,说明盘被限速了,别折腾编译安装
八、到底怎么选
- 选极速安装:标准 LNMP 业务、WordPress / 宝塔建站、不想维护编译参数、系统盘小于 40G、需要面板一键升级。这是 95% 的场景。
- 选编译安装:需要
--add-module第三方模块(lua、concat、geoip2)、需要固定 OpenSSL 版本、需要--with-swoole这类深度绑定扩展、或者你想吃-march=native那 2%。 - 混搭方案:Nginx 走极速安装(省事),PHP 单独编译一个版本共存(宝塔支持多版本 PHP),用哪个站挂哪个版本。这是我在生产里最常用的组合,既不折腾 Nginx 的升级链,又能拿到自定义扩展。
最后一句:别信"编译安装性能翻倍",那是不懂的人在传。真正决定你这台 VPS 性能的,是 opcache.jit_buffer_size 有没有写对、proxy_max_temp_file_size 有没有关、以及磁盘 4K 随机写实不实。这三件事做完,极速安装的机器不会比编译安装慢。