服务器配置:雨云十堰4核4G,Debian 13
网站:Zibll 主题 + WordPress 7.0 + PHP 8.5+EdgeONE
只是将我个人环境和优化过程分享给大家,实际需要根据真实情况进行略微调整,例如可不使用redis集群
优化前:
打开一个页面要 610 毫秒以上,每次请求要查 7~10 次数据库,人一多直接 503。
优化后:
未登录的游客打开页面只要 1.5 毫秒,登录用户 16 毫秒,SQL 查询降到 3 次,500 个人同时访问全部成功,没有一个报错。
下面我就把这套完整的优化过程拆开,用大白话讲清楚每个地方改了什么、为什么要改、具体怎么操作。不管你是用宝塔面板还是纯命令行,都可以跟着做。
一、Redis Sentinel 高可用集群(缓存永不断)
为什么搞这个?
1.1 整体架构
画个图就好懂了:
你的 WordPress(PHP)
↓
HAProxy 端口 6390(自动找谁是 Master,只往 Master 写)
↓
3 个哨兵(Sentinel) 互相监控,一旦 Master 挂了,5 秒内投票选新 Master
↓
4 个 Redis 节点(:6379 宝塔自带的,:6380 :6381 :6382 是 Docker 跑的)
平时 1 主 3 从,挂了自动切
简单说:不管哪个 Redis 挂掉,WordPress 完全感觉不到,因为 HAProxy 会自动把请求转到新老大。
1.2 怎么搭?
所有配置文件都放在 /www/wwwroot/redis-sentinel-ha/ 下面。
docker-compose.yml 里定义了 7 个容器:
-
redis-master(端口 6380) -
redis-slave-1(端口 6381) -
redis-slave-2(端口 6382) -
redis-sentinel-1/2/3(哨兵,分别用 26379、26380、26381) -
redis-haproxy(代理,端口 6390)
宝塔装的那个 Redis(端口 6379)也用 replicaof 命令加入这个集群,变成第 4 个从节点。
关键 Redis 参数:
-
每个节点最多用 384MB 内存(总共 1.5GB 缓存空间)
-
满了就按最近最少使用淘汰(
allkeys-lru) -
关闭持久化(纯缓存用,不要 RDB/AOF,快了不止一点)
-
开启主动碎片整理和异步删除,防止内存膨胀
HAProxy 的配置也很聪明:它定期给每个 Redis 发 INFO replication 命令,看谁回复里带 role:master,就只把请求发给它。这样主从切了也没事。
故障测试:故意杀掉 Master 节点,5 秒后哨兵们投票,从节点自动升级成新 Master,网站全程没断过。
1.3 Redis DB 分配
为了不同业务互不干扰,我们把不同数据存在不同编号的库里:
-
DB0:放 PHP Session(用户登录状态)
-
DB1:主站点缓存(对象缓存 + 页面缓存)
二、Linux 系统内核调优(让系统更能扛)
2.1 改内核参数
新建文件 /etc/sysctl.d/99-perf-optimize.conf,把下面这些粘进去:
# 网络相关
net.core.somaxconn = 4096
net.core.netdev_max_backlog = 8192
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.ip_local_port_range = 10000 65535
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_max_tw_buckets = 8192
net.ipv4.tcp_rmem = 4096 87380 8388608
net.ipv4.tcp_wmem = 4096 65536 8388608
# 虚拟内存
vm.swappiness = 1
vm.overcommit_memory = 1
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
# 文件句柄
fs.file-max = 1048576
然后执行 sysctl -p 让它生效。
大白话解释:
-
somaxconn和 backlog 都放大,就是让系统能同时接待更多排队连接,别动不动就拒绝。 -
加快 TIME_WAIT 状态的回收,避免端口被占满。
-
把 swap 倾向调到 1,意思是尽量用物理内存,别用硬盘充当内存,不然网站会突然卡成狗。
-
文件句柄数调大,免得高并发时报 “too many open files”。
2.2 修改文件限制
新建 /etc/security/limits.d/99-perf.conf:
* soft nofile 655360
* hard nofile 655360
* soft nproc 65535
* hard nproc 65535
这样一来,Nginx、PHP、MySQL 都能打开足够多的文件。
顺便把透明大页(THP)关掉,这东西会导致内存分配延迟突然飙升
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
IO 调度器改成 mq-deadline(适合云服务器)。
实测效果:优化后 TIME_WAIT 连接数从 1600 多降到 600 多,系统负载从 1.9 降到了 0.72。
三、MySQL 5.7 优化(榨干数据库性能)
3.1 改 /etc/my.cnf
核心就是给 InnoDB 分配更多内存当缓存,关闭查询缓存(MySQL 5.7 开了反而慢),调大各种缓冲区。我直接列出变化对比:
| 参数 | 旧值 | 新值 | 干啥的 |
|---|---|---|---|
| innodb_buffer_pool_size | 64M | 1G | 给 InnoDB 1G 内存当缓存,数据库总共才 256M,全装进去 |
| max_connections | 100 | 300 | 允许更多同时连接 |
| query_cache_size | 32M | 0 | 关闭查询缓存,避免锁竞争 |
| skip-name-resolve | OFF | ON | 不反解 DNS,连接更快 |
| innodb_flush_method | - | O_DIRECT | 绕过系统缓存,避免双重缓冲 |
| innodb_flush_log_at_trx_commit | 1 | 2 | 每秒刷一次日志,性能提升 2 倍,丢 1 秒数据可接受 |
| innodb_io_capacity | 200 | 1000 | 告诉 MySQL 磁盘能跑多少 IOPS,云盘设 1000 |
| sort_buffer_size | 256K | 4M | 排序更快 |
| join_buffer_size | 512K | 4M | 连表查询更快 |
| tmp_table_size | 32M | 128M | 临时表放内存,不写磁盘 |
| sync_binlog | 1 | 0 | binlog 由系统决定何时刷盘,减少磁盘写入 |
| long_query_time | 3 | 1 | 超过 1 秒就记慢查询 |
| expire_logs_days | 10 | 3 | binlog 只保留 3 天 |
改完重启 MySQL。
3.2 清理数据库垃圾
WordPress 用久了里面一堆废数据,直接跑 SQL 清理:
-- 删掉文章修订版
DELETE FROM wp_posts WHERE post_type = 'revision';
-- 删除孤立的元数据(文章删了但 meta 还在)
DELETE pm FROM wp_postmeta pm LEFT JOIN wp_posts p ON pm.post_id = p.ID WHERE p.ID IS NULL;
-- 删除过期的临时数据
DELETE FROM wp_options WHERE option_name LIKE '%_transient_timeout_%' AND option_value < UNIX_TIMESTAMP();
-- 优化碎片
OPTIMIZE TABLE wp_options, wp_posts, wp_postmeta, wp_usermeta;
3.3 清理 autoload 膨胀
WordPress 每次请求都会把所有 autoload=yes 的 option 从数据库加载到内存。我一看吓一跳,有 1111 条,占 828KB。实际很多是已经卸载的插件和主题留下的垃圾。
手动清理掉已经卸载的主题配置、插件配置,优化后剩下 813 条,356KB,直接少了一半多。
效果:缓存命中率 99.88%,慢查询归零,临时表写磁盘的现象几乎没了。
四、PHP 8.5 优化(让代码飞起来)
4.1 OPcache(代码缓存)
PHP 默认每次请求都要把 .php 文件读进来编译一遍,傻得要命。开 OPcache 后,编译好的字节码常驻内存,重复请求直接拿现成的,快了不知道多少倍。
编辑 /www/server/php/85/etc/php.ini 的 [opcache] 部分:
opcache.memory_consumption = 256
opcache.max_accelerated_files = 20000
opcache.interned_strings_buffer = 32
opcache.revalidate_freq = 60
opcache.enable_file_override = 1
opcache.fast_shutdown = 1
-
给 OPcache 256M 内存,能缓存 2 万个文件(全站 PHP 文件才 1500 多个)
-
60 秒检查一次文件是否更新(线上没所谓,开发时嫌慢可以手动清缓存)
-
打开 fast_shutdown 能稍微再快一点
打开后缓存命中率直接拉到 99.99%。
4.2 JIT(即时编译)
PHP 8 加的 JIT 能让热点代码直接变成本地机器码,省去解释执行的开销。不过 WordPress 里大量用了 eval 和动态调用,JIT 能优化的地方有限,但聊胜于无,开了总比不开强。
opcache.jit = tracing
opcache.jit_buffer_size = 128M
opcache.jit_hot_func = 127
opcache.jit_hot_loop = 61
注意:这台服务器有个内核 bug,huge_code_pages 如果打开会导致 PHP 崩溃,必须设为 0。
4.3 PHP-FPM 进程管理
宝塔默认是 ondemand 模式,没请求时进程全死,来请求了现创建,浪费几十毫秒。改成静态模式,固定起 20 个进程候着。
/www/server/php/85/etc/php-fpm.conf:
pm = static
pm.max_children = 20
pm.max_requests = 1000
listen.backlog = 8192
固定 20 个进程,每个大概占 100MB,总共 2G,4G 内存绰绰有余。
-
每个进程处理 1000 个请求后自动重启,防止内存泄漏。
-
监听队列设大,不怕瞬时流量。
4.4 其他
-
把单脚本内存上限从 128M 提到 256M,给 Zibll 主题足够空间。
-
关掉前台显示错误(防止泄露路径),打开 session 懒写(Lazy write,减少 IO)。
-
把 PHP session 存到 Redis 集群里(
session.save_path = "tcp://127.0.0.1:6390"),这样主从切换时登录状态也不丢。
五、Nginx 优化(把静态资源和匿名访问变成火箭)
5.1 Gzip 压缩
文本文件压缩再发给浏览器,一下子能省 80% 流量。在 nginx.conf 的 http 块里加上:
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain application/javascript text/css application/xml application/json image/svg+xml;
gzip_vary on;
gzip_proxied any;
原本首页 216KB,压缩后只传 34KB,减少了 84%。
5.2 静态文件缓存
图片、CSS、JS、字体这些文件万年不变,直接让浏览器缓存起来,下次直接读本地,请求都不发。
在站点的 Nginx 配置文件里加:
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; access_log off; }
location ~ .*\.(js|css)?$ { expires 12h; access_log off; }
location ~ .*\.(eot|otf|ttf|woff|woff2|svg)$ { expires 365d; access_log off; }
还把记录这些静态文件的访问日志也关了,省磁盘 IO。
5.3 FastCGI Cache(这是给游客的大杀器)
这是什么? Nginx 可以把 PHP 执行完生成的 HTML 页面直接存成文件,下次有同样的请求直接返回这个文件,根本不经过 PHP 和 MySQL,快得离谱。
怎么开?
先在全局配置里定义缓存目录和参数(/www/server/panel/vhost/nginx/0.fastcgi_cache.conf):
fastcgi_cache_path /www/server/fastcgi_cache levels=1:2 keys_zone=WORDPRESS:128m inactive=30m max_size=512m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_lock on;
然后写一个专门的 PHP 处理器配置 enable-php-85-cache.conf,里面加了很多判断条件:
-
如果是 POST 请求,不缓存
-
如果是搜索结果页,不缓存
-
如果有
utm_等跟踪参数,不缓存 -
如果 URL 包含
/wp-admin、/login、/cart等动态地址,不缓存 -
如果 Cookie 里有
wordpress_logged_in,说明是登录用户,不缓存(因为他们看到的东西不一样)
全部不满足,就认为是个普通的游客访问,直接把返回的 HTML 存缓存,下次直接给。
同时在响应头里加个 X-Cache-Status,方便看命中没命中。
效果:游客打开页面从 610ms 直接变成 1.5ms,快了整整 400 倍!
六、WordPress 本身优化(从代码层面再砍一刀)
6.1 wp-config.php 加几行
define('DISABLE_WP_CRON', true); // 禁用自带定时任务,改成系统 crontab 跑
define('WP_POST_REVISIONS', 3); // 只保留 3 个修订版
define('AUTOSAVE_INTERVAL', 120); // 两分钟自动保存一次
define('EMPTY_TRASH_DAYS', 3); // 回收站只保留 3 天
define('DISALLOW_FILE_EDIT', true); // 禁止后台编辑文件,安全
define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '512M');
然后把 WP-Cron 交给系统 crontab:
*/5 * * * * /www/server/php/85/bin/php /www/wwwroot/<你的目录>/wp-cron.php --allow-root >/dev/null 2>&1
6.2 Object Cache Pro 直连 Redis HA
WordPress 的对象缓存插件我用的是 Object Cache Pro,配置里直接把 Redis 地址写成 HAProxy 的 6390 端口:
define('WP_REDIS_CONFIG', [
'host' => '127.0.0.1',
'port' => 6390,
'database' => 1,
'persistent' => true, // 长连接,省去每次连接开销
'timeout' => 0.5,
'compression' => 'zstd',
'serializer' => 'igbinary',
'async_flush' => true,
'split_alloptions' => true,
'prefetch' => true,
]);
6.3 Must-Use 插件:两把性能小刀
在 /wp-content/mu-plugins/ 下放两个文件,它们会自动执行,不用后台启用。
000-perf-optimize.php:干掉所有没用但费性能的东西。
-
禁止 WordPress 每次加载页面时检查核心/插件/主题更新(每个请求省了至少一条 UPDATE 查询)
-
去掉头部无用的 link(rsd、wlwmanifest、短链接、Emoji 脚本等)
-
禁用 XML-RPC 的 pingback 功能(防攻击又省资源)
-
关闭 Object Cache 的统计上报(省去分析开销)
001-page-cache.php:给登录用户也做页面缓存! 游客有 Nginx 缓存了,登录用户因为 Cookie 被跳过了,怎么办?这个插件在 WordPress 启动前就检查 Redis 里有没有存好的 HTML,有就立刻返回,完全不碰 PHP 和数据库。
-
缓存 key 里带用户哈希,不同用户缓存独立
-
只缓存 GET 请求,自动跳过后台、搜索、支付等动态页面
-
TTL 设 120 秒,过期自动重建
-
一旦用户执行了 POST 操作(发评论、修改资料),在请求结束时自动清掉该用户的所有缓存,保证数据是最新的
效果:登录用户首次打开 500ms,缓存命中后降到 16~38ms,快了 20~30 倍!
七、最终效果,用数据说话
8.1 性能对比表
| 场景 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 游客访问(缓存命中) | 610ms | 1.5ms | 400倍 |
| 登录用户首次/缓存未命中 | 610ms | 500ms | 快 22% |
| 登录用户缓存命中 | 610ms | 16~38ms | 20~38倍 |
| SQL 查询次数 | 7~10次 | 3次 | 减少 70% |
| 200 并发测试 | 大量 503 | 全部成功 | - |
| 500 并发测试 | 全部 503 | 全部成功 | - |
| Gzip 压缩率 | 未开启 | 84% | 216KB→34KB |
| OPcache 命中率 | 0% | 99.99% | - |
| autoload 数据量 | 828KB | 356KB | 减少 57% |
8.2 缓存怎么配合的
一个请求进来:
├─ 是游客,且是 GET 普通页面
│ └─ Nginx 直接给 HTML(1.5ms),没命中才去叫 PHP 生成
├─ 是登录用户,且是 GET 普通页面
│ └─ MU 插件从 Redis 取 HTML(16ms),没命中才启动 WordPress
├─ 是 POST / PUT / DELETE 操作
│ └─ 正常走 PHP,处理完自动把该用户的页面缓存全清掉
└─ 是后台 / API / 搜索等特殊地址
└─ 老实走 PHP,不缓存
8.3 压力测试
内网用 curl 模拟 500 个并发请求游客首页,缓存命中后 3.2 秒全部完成,成功率 100%,相当于每秒处理 156 个请求。登录用户的缓存命中后,平均响应 28ms。
九、跟着做的完整命令(复制粘贴版)
注意:尖括号里的内容改成你自己的真实值,比如 <你的域名>。
9.1 搭建 Redis Sentinel 集群
mkdir -p /www/wwwroot/redis-sentinel-ha
# 把写好的 docker-compose.yml 和 haproxy.cfg 上传到这个目录
chmod 644 /www/wwwroot/redis-sentinel-ha/haproxy.cfg
# 备份宝塔 Redis 配置
cp /www/server/redis/redis.conf /www/server/redis/redis.conf.bak
# 编辑 redis.conf,在末尾加上:
# replicaof 127.0.0.1 6380
# replica-announce-ip 127.0.0.1
# replica-announce-port 6379
# maxmemory 384mb
# maxmemory-policy allkeys-lru
# appendonly no
# activedefrag yes
# 启动 Docker 集群
cd /www/wwwroot/redis-sentinel-ha
docker compose up -d
# 重启宝塔 Redis(以从节点身份加入)
redis-cli -p 6379 SHUTDOWN NOSAVE
/www/server/redis/src/redis-server /www/server/redis/redis.conf
# 检查集群状态
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
redis-cli -p 6390 PING
9.2 PHP Session 改到 Redis 集群
sed -i 's|session.save_path = "tcp://127.0.0.1:6379"|session.save_path = "tcp://127.0.0.1:6390"|' /www/server/php/85/etc/php.ini
sed -i 's|session.save_path = "tcp://127.0.0.1:6379"|session.save_path = "tcp://127.0.0.1:6390"|' /www/server/php/85/etc/php-cli.ini
/etc/init.d/php-fpm-85 restart
9.3 启用 Nginx FastCGI 缓存
# 把站点 Nginx 配置里的 PHP 处理器引用改成带缓存的版本
sed -i 's|include enable-php-85.conf;|include enable-php-85-cache.conf;|' /www/server/panel/vhost/nginx/<你的域名>.conf
# 检查配置并重载
nginx -t && /etc/init.d/nginx reload
# 验证:第一次访问是 MISS,第二次就是 HIT
curl -sS -I -H "Host: <你的域名>" http://127.0.0.1/ | grep X-Cache
9.4 清理优化数据库
MYSQL_PW="你的数据库root密码"
DB="你的数据库名"
TP="wp_"
mysql -uroot -p"$MYSQL_PW" "$DB" -e "
DELETE FROM ${TP}posts WHERE post_type='revision';
DELETE pm FROM ${TP}postmeta pm LEFT JOIN ${TP}posts p ON pm.post_id=p.ID WHERE p.ID IS NULL;
DELETE FROM ${TP}options WHERE option_name LIKE '%_transient_timeout_%' AND option_value < UNIX_TIMESTAMP();
OPTIMIZE TABLE ${TP}options, ${TP}posts, ${TP}postmeta, ${TP}usermeta, ${TP}users, ${TP}comments;
"
# 清理 autoload 垃圾(根据你网站情况调整)
mysql -uroot -p"$MYSQL_PW" "$DB" -e "
DELETE FROM ${TP}options WHERE option_name LIKE 'litespeed%';
DELETE FROM ${TP}options WHERE option_name LIKE '%supercache%';
DELETE FROM ${TP}options WHERE option_name LIKE 'wpseo_%';
OPTIMIZE TABLE ${TP}options;
"
9.5 应用 PHP OPcache + JIT 配置
修改好 php.ini 后:
/etc/init.d/php-fpm-85 restart
php -i | grep "opcache.jit " # 输出应包含 tracing
9.6 安装 MU-Plugin
mkdir -p /www/wwwroot/<你的目录>/wp-content/mu-plugins
# 把 000-perf-optimize.php 和 001-page-cache.php 上传进去
chmod 644 /www/wwwroot/<你的目录>/wp-content/mu-plugins/*.php
chown www:www /www/wwwroot/<你的目录>/wp-content/mu-plugins/*.php
/etc/init.d/php-fpm-85 restart
000-perf-optimize.php
<?php
/**
* 性能优化 MU-Plugin
* 禁用不必要的功能和查询,减少每次页面加载的开销
*/
// 1. 禁用 WordPress 核心、插件、主题的更新检查
// 这些检查每次页面加载都会往数据库写 UPDATE 查询,纯属浪费
remove_action('init', 'wp_schedule_update_checks');
// 短路更新检查的 transient,直接返回空结果,让 WordPress 以为没有任何更新
add_filter('pre_site_transient_update_core', function () {
return (object) array(
'last_checked' => time(),
'version_checked' => '7.0',
'updates' => array(),
);
});
add_filter('pre_site_transient_update_plugins', function () {
return (object) array(
'last_checked' => time(),
'response' => array(),
'translations' => array(),
);
});
add_filter('pre_site_transient_update_themes', function () {
return (object) array(
'last_checked' => time(),
'response' => array(),
'translations' => array(),
);
});
// 2. 移除网页头部(head)里一堆没用的链接标签
remove_action('wp_head', 'rsd_link'); // Really Simple Discovery
remove_action('wp_head', 'wlwmanifest_link'); // Windows Live Writer
remove_action('wp_head', 'wp_generator'); // WordPress 版本号
remove_action('wp_head', 'wp_shortlink_wp_head'); // 短链接
remove_action('wp_head', 'rest_output_link_wp_head', 10); // REST API 链接
remove_action('wp_head', 'wp_oembed_add_discovery_links'); // oEmbed 发现链接
remove_action('wp_head', 'wp_oembed_add_host_js'); // oEmbed JS
remove_action('wp_head', 'feed_links_extra', 3); // 分类 Feed 等
remove_action('wp_head', 'feed_links', 2); // 通用 Feed
// 3. 禁用 Emoji 相关的脚本和样式
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');
// 4. 关闭 Object Cache Pro 的统计上报(省去每次请求的分析开销)
add_filter('objectcache.analytics', '__return_false');
// 5. 禁用 XML-RPC 的 pingback(安全 + 省资源)
add_filter('xmlrpc_methods', function ($methods) {
unset($methods['pingback.ping']);
return $methods;
});
001-page-cache.php
<?php
/**
* Redis 页面缓存(针对登录用户)
*
* 缓存策略:
* - Key 格式:pgc:<用户哈希>:<md5(域名+URI)>
* - TTL:120 秒
* - 任何 POST/PUT/DELETE 请求结束后,自动清除当前用户所有缓存
*
* 注意:游客的页面缓存由 Nginx fastcgi_cache 负责,这个插件只处理登录用户。
*/
$is_get = (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'GET');
// 不处理 AJAX、Cron、XML-RPC、REST 请求和后台页面
if (
(defined('DOING_AJAX') && DOING_AJAX) ||
(defined('DOING_CRON') && DOING_CRON) ||
(defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) ||
(defined('REST_REQUEST') && REST_REQUEST) ||
is_admin()
) {
return;
}
// ========== 非 GET 请求:注册清除缓存回调,然后结束 ==========
if (!$is_get) {
function _pgcache_invalidate() {
$user_hash = 'anon';
foreach ($_COOKIE as $name => $value) {
if (strpos($name, 'wordpress_logged_in') === 0) {
$user_hash = 'u' . substr(md5($name . $value), 0, 16);
break;
}
}
try {
$r = new Redis();
if ($r->connect('127.0.0.1', 6390, 0.3)) {
$r->select(1); // DB1 存放页面缓存
$keys = $r->keys("pgc:$user_hash:*");
if (!empty($keys)) {
$r->del($keys);
}
$r->close();
}
} catch (Exception $e) {
// 静默失败,不影响正常请求
}
}
register_shutdown_function('_pgcache_invalidate');
return;
}
// ========== 以下是 GET 请求处理 ==========
// 1. 检查是否是需要跳过的路径
$uri = $_SERVER['REQUEST_URI'] ?? '';
$skip_paths = [
'/wp-admin', '/wp-login', '/wp-json', '/xmlrpc', '/wp-cron',
'/feed', '/sitemap', '/wp-comments', '/cart', '/checkout',
'/netcard', '/user-sign', '/search', '/oauth', '/newposts', '/bbs-edit',
'/author/', '/message', '/notification', '/notify',
'/uc_', '/user-center', '/my-account', '/dashboard',
'/wp-content/uploads', '/cdn-cgi',
];
foreach ($skip_paths as $p) {
if (stripos($uri, $p) !== false) {
return; // 动态页面不缓存
}
}
// 2. 检查查询参数,跳过含有跟踪参数或特殊参数的请求
$query = $_SERVER['QUERY_STRING'] ?? '';
if (preg_match('/(utm_|preview|gclid|fbclid|nonce|_wpnonce|add-to-cart|p=|action=)/', $query)) {
return;
}
// 允许分页、排序等简单参数,其他复杂查询不缓存
if (!empty($query) && !preg_match('/^(paged|page|cat|tag|orderby|order|type|status|tab)=/', $query)) {
return;
}
// 3. 生成缓存 Key
function _pgcache_key() {
$user_hash = 'anon';
foreach ($_COOKIE as $name => $value) {
if (strpos($name, 'wordpress_logged_in') === 0) {
$user_hash = 'u' . substr(md5($name . $value), 0, 16);
break;
}
}
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
return 'pgc:' . $user_hash . ':' . md5($host . ($_SERVER['REQUEST_URI'] ?? '/'));
}
// 4. 尝试从 Redis 取出缓存并直接输出
function _pgcache_try_serve() {
$key = _pgcache_key();
try {
$r = new Redis();
if (!$r->connect('127.0.0.1', 6390, 0.3)) {
return;
}
$r->select(1);
$html = $r->get($key);
$r->close();
if ($html && strlen($html) > 500) {
// 简单判断是不是 HTML 内容
if (substr($html, 0, 1) === '<' || substr($html, 0, 9) === '<!DOCTYPE') {
header('Content-Type: text/html; charset=UTF-8');
header('X-Page-Cache: HIT');
echo $html;
exit;
}
}
} catch (Exception $e) {
// 静默失败
}
if (!headers_sent()) {
header('X-Page-Cache: MISS');
}
}
_pgcache_try_serve();
// 5. 如果缓存未命中,等 WordPress 生成完页面后,把结果存进 Redis
ob_start(function ($html) {
if (http_response_code() !== 200) {
return $html;
}
if (strlen($html) < 1000) {
return $html; // 太短的内容不缓存(可能是错误页)
}
if (stripos($html, '<html') === false && stripos($html, '<!DOCTYPE') === false) {
return $html; // 不是完整 HTML,不缓存
}
$key = _pgcache_key();
try {
$r = new Redis();
if ($r->connect('127.0.0.1', 6390, 0.3)) {
$r->select(1);
$r->setex($key, 120, $html); // 120 秒过期
$r->close();
}
} catch (Exception $e) {
// 静默失败
}
return $html;
});
9.7 全量验证
# 1. 服务状态
docker ps | grep redis
ps aux | grep php-fpm | grep pool | wc -l # 应该是 20
redis-cli -p 6390 PING
mysql -uroot -p -e "SELECT 1"
# 2. 缓存测试
curl -sS -I -H "Host: <你的域名>" http://127.0.0.1/ | grep X-Cache
# 游客 → X-Cache-Status: HIT
curl -sS -I -H "Host: <你的域名>" -H "Cookie: wordpress_logged_in_test=1" http://127.0.0.1/ | grep X-Page-Cache
# 登录用户 → X-Page-Cache: HIT
# 3. 速度测试
curl -sS -o /dev/null -w "响应时间: %{time_total}s\n" -H "Host: <你的域名>" http://127.0.0.1/
# 4. 简单并发测试
for i in {1..200}; do curl -sS -o /dev/null -w "%{http_code}\n" -H "Host: <你的域名>" http://127.0.0.1/ & done; wait
# 应该全是 200
# 5. Redis 主从状态
for port in 6379 6380 6381 6382; do echo "端口 $port 角色: $(redis-cli -p $port INFO replication | grep ^role:)"; done
十、避坑和注意事项
-
huge_code_pages 必须关:这台服务器内核有 bug,开了 PHP 会挂。
-
JIT 效果有限:WordPress 太多动态特性,不能完全指望 JIT,但开着无害。
-
页面缓存 120 秒过期:不怕内容更新不及时,因为任何 POST 操作都会立刻清除缓存。
-
OPcache 每 60 秒检查文件:改代码后最慢 1 分钟生效,着急的话手动重启 PHP。
-
宝塔会覆盖配置:在宝塔面板里改过 PHP/MySQL 设置后,记得回头检查我们手动改的那些参数有没有被复原,最好在面板里也同步设置一下。
-
如果你有多个站点:它们共用同一个 fastcgi_cache 缓存区,通过域名自动区分,不会串。
-
固定链接用数字 ID:
/%post_id%.html比文章名查询快得多,数据库压力更小。
整套优化从系统底层到应用层全都过了一遍,核心思路就是“能缓存就缓存,能提前准备好就不现做,能用内存就别用磁盘
广告:














暂无评论内容