当 Nginx + PHP-FPM + MySQL 部署 WordPress 后出现 MySQL CPU 占用异常高(如持续 >80%、频繁 100%),需系统性诊断,避免盲目调参或重启。以下是结构化、实战导向的诊断流程(含命令、SQL、日志分析和 WordPress 特定优化点):
🔍 一、快速确认现象 & 排除干扰
# 1. 确认确实是 mysqld 进程导致(非其他进程伪装)
top -c | grep mysql
htop # 查看实际 CPU 占用进程树
# 2. 检查是否为瞬时高峰(如定时任务、备份、爬虫)
watch -n 1 'mysql -e "SHOW PROCESSLIST;" | wc -l' # 观察连接数波动
sar -u 1 10 # 查看 CPU 使用趋势(需 sysstat)
✅ 关键判断:若
mysqld常驻高 CPU 且SHOW PROCESSLIST中存在大量Sleep或长时间Query,则进入深度诊断。
🛠️ 二、MySQL 层诊断(核心步骤)
1. 实时观察活跃查询(最有效入口)
-- 登录 MySQL(建议用管理员账号)
mysql -u root -p
-- 查看正在执行的慢/长耗时查询(重点关注 State=Sending data, Sorting result, Copying to tmp table)
SHOW FULL PROCESSLIST;
-- 更清晰视图(按时间倒序,过滤非 Sleep)
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO
FROM information_schema.PROCESSLIST
WHERE COMMAND != 'Sleep' AND TIME > 3
ORDER BY TIME DESC LIMIT 20;
2. 捕获慢查询(必须开启!)
-- 检查慢查询日志是否启用
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time'; -- 默认10s,WordPress建议设为1~2s
SHOW VARIABLES LIKE 'slow_query_log_file';
-- 若未启用,临时开启(重启后失效,生产环境请写入 my.cnf)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1.0;
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
✅ 立即操作:
- 将
long_query_time设为1.0(WordPress 多数查询应 <1s) - 等待 5–10 分钟,然后分析慢日志:
# 分析慢日志(需 mysqldumpslow) mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log # 或用 pt-query-digest(更强大,推荐) pt-query-digest /var/log/mysql/mysql-slow.log | head -50
3. 检查索引缺失与低效查询(WordPress 典型问题)
-- 查看是否有全表扫描(Rows_examined 远大于 Rows_sent)
SELECT query, rows_examined, rows_sent, tmp_tables, full_scan
FROM performance_schema.events_statements_summary_by_digest
WHERE full_scan = 'YES' AND last_seen > DATE_SUB(NOW(), INTERVAL 1 HOUR)
ORDER BY rows_examined DESC LIMIT 10;
-- 检查高频慢查询的执行计划(替换为你的慢SQL)
EXPLAIN FORMAT=JSON SELECT * FROM wp_posts WHERE post_status='publish' AND post_type='post' ORDER BY post_date DESC LIMIT 10;
⚠️ WordPress 高危 SQL 模式(重点检查):
SELECT * FROM wp_posts WHERE post_status='publish' ORDER BY post_date DESC LIMIT 10→ 缺少(post_status, post_date)复合索引SELECT * FROM wp_postmeta WHERE meta_key = '_wp_attached_file'→meta_key无索引(WordPress 5.0+ 已修复,旧版需手动加)JOIN wp_posts p JOIN wp_postmeta pm ON p.ID = pm.post_id WHERE pm.meta_key='xxx'→ 未利用索引关联
🔧 修复示例:
-- 为 wp_postmeta 添加索引(WordPress 官方推荐)
ALTER TABLE wp_postmeta ADD INDEX idx_meta_key (meta_key(191));
-- 为 wp_posts 添加复合索引(首页/归档页提速)
ALTER TABLE wp_posts ADD INDEX idx_status_type_date (post_status, post_type, post_date);
4. 检查连接与资源瓶颈
-- 查看连接数是否超限(max_connections 默认151,WordPress高并发易打满)
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
-- 检查临时表使用(频繁磁盘临时表 = CPU+IO双高)
SHOW STATUS LIKE 'Created_tmp%';
-- 关注 Created_tmp_disk_tables > 0 且持续增长 → 调大 tmp_table_size/max_heap_table_size
-- 检查缓冲区是否过小(导致频繁磁盘读)
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- 建议:专用DB服务器设为物理内存的 70%~80%
🌐 三、WordPress 层协同诊断(常被忽略!)
1. 插件/主题导致的数据库风暴
- 禁用所有插件:通过重命名
/wp-content/plugins/文件夹,测试 CPU 是否下降 - 切换默认主题(Twenty Twenty-Four)排除主题 SQL 问题
- 检查恶意插件:常见“SEO”、“缓存”、“统计”插件可能每页执行数十次查询
2. WordPress 自身低效行为
-
WP_Query 未分页/未缓存:
// ❌ 危险!获取全部文章(万级数据时全表扫描) $posts = get_posts(['post_type'=>'any', 'posts_per_page'=>-1]); // ✅ 正确:限制数量 + 使用缓存 $posts = get_posts(['post_type'=>'post', 'posts_per_page'=>12, 'cache_results'=>true]); -
未使用对象缓存:
- 安装 Redis/Memcached + Redis Object Cache 插件,可减少 70%+ 数据库查询
- 检查
wp_options表中alloptions是否过大(>1MB),会导致每次请求加载全表
3. 爬虫/暴力请求攻击
# 检查 Nginx 日志中高频访问 wp-login.php 或 xmlrpc.php
zgrep -E "(wp-login.php|xmlrpc.php)" /var/log/nginx/access.log* | awk '{print $1}' | sort | uniq -c | sort -nr | head -20
# 临时封禁(配合 fail2ban 或 nginx limit_req)
⚙️ 四、系统与配置层检查
| 项目 | 检查命令 | 风险点 |
|---|---|---|
| MySQL 配置合理性 | mysqladmin var | grep -E "(innodb_buffer_pool|tmp_table|query_cache)" |
query_cache_type=1 在高并发下反而降低性能(MySQL 5.7+ 建议关闭) |
| 磁盘 I/O 瓶颈 | iostat -x 1 5 |
await > 50ms 且 %util > 90% → SSD 故障或 RAID 卡电池失效 |
| PHP-FPM 过载传导 | ps aux | grep php-fpm | wc -l |
连接数 > pm.max_children → 请求堆积到 MySQL(表现为 MySQL 等待连接) |
🚀 五、立竿见影的优化措施(按优先级)
- 紧急止血
-- 临时终止长期运行查询(ID 来自 PROCESSLIST) KILL 12345; - 必做索引(WordPress 核心修复)
ALTER TABLE wp_postmeta ADD INDEX idx_post_id_meta_key (post_id, meta_key(191)); ALTER TABLE wp_posts ADD INDEX idx_status_date (post_status, post_date); - 启用 OPcache + Redis 对象缓存
- PHP:
opcache.enable=1,opcache.memory_consumption=256 - WordPress: 安装 Redis Object Cache 并启用
- PHP:
- Nginx 缓存静态资源 + FastCGI 缓存(减少 PHP-FPM 和 MySQL 调用)
# 示例:缓存 WordPress 页面(需配合插件或自定义逻辑) fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
📋 附:诊断清单(Checklist)
- [ ]
slow_query_log已开启且long_query_time ≤ 2 - [ ]
SHOW PROCESSLIST中无 >5s 的活跃查询 - [ ]
wp_postmeta.meta_key和wp_posts.post_status+post_date索引已创建 - [ ] WordPress 启用 Redis 对象缓存(非仅页面缓存)
- [ ] Nginx 开启
fastcgi_cache或使用 WP Super Cache / WP Rocket - [ ] 检查并禁用可疑插件(尤其“实时统计”、“链接检查器”类)
- [ ]
innodb_buffer_pool_size≥ 物理内存 70%(专用 DB 服务器)
💡 终极提示:
80% 的 WordPress MySQL 高 CPU 问题源于插件滥用或缺失关键索引,而非 MySQL 配置本身。先做SHOW PROCESSLIST+ 慢日志分析,再动手调优,避免“调参式运维”。
如需进一步分析,请提供:
① mysqldumpslow -t 10 /path/to/slow.log 输出片段
② SHOW CREATE TABLE wp_posts; 和 wp_postmeta; 结果
③ top 中 mysqld 的 %CPU 和线程数截图
我可为你定制优化方案。
云知识CLOUD