当 Nginx + PHP-FPM + MySQL 部署 WordPress 后出现 MySQL CPU 占用异常高(如持续 >80%、频繁 100%),需系统性诊断。以下是实战导向的分步诊断与优化指南,兼顾效率与深度:
🔍 一、快速确认问题范围(5分钟内)
# 1. 确认是否真是 MySQL 导致(而非 PHP 或 I/O)
top -c # 观察 %CPU 列,看 mysqld 进程是否主导
htop | grep mysqld # 更直观(需安装 htop)
# 2. 检查 MySQL 实际负载(非仅 CPU)
mysqladmin -u root -p processlist -v # 查看当前活跃连接和慢查询
mysqladmin -u root -p extended-status | grep -E "Threads_connected|Threads_running|Questions|Slow_queries"
✅ 关键指标关注:
Threads_running > 10→ 并发执行中查询过多Slow_queries > 0→ 存在未优化查询Threads_connected持续高位 → 连接泄漏或连接池配置不当
🛠️ 二、精准定位高 CPU 根源(核心步骤)
✅ 步骤 1:启用并分析慢查询日志(最有效!)
-- 登录 MySQL 执行:
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5; -- 记录 >500ms 的查询(WordPress 常见瓶颈阈值)
SET GLOBAL log_queries_not_using_indexes = ON; -- 记录未走索引的查询
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
⚠️ 生产环境建议:先设
long_query_time=1,观察 10 分钟后调低;避免日志爆炸。
分析慢日志(推荐工具):
# 安装 pt-query-digest(Percona Toolkit)
sudo apt install percona-toolkit # Ubuntu/Debian
sudo yum install percona-toolkit # CentOS/RHEL
# 生成报告(按响应时间排序 TOP 10 查询)
pt-query-digest /var/log/mysql/slow.log | head -n 50
# 或直接查看高频慢查询(WordPress 典型问题):
grep -A 5 "SELECT.*wp_" /var/log/mysql/slow.log | grep -E "(COUNT|ORDER BY|JOIN)" | head -20
| 📌 WordPress 常见高 CPU 查询模式: | 类型 | 示例 | 原因 |
|---|---|---|---|
| 未索引的 meta 查询 | SELECT * FROM wp_postmeta WHERE meta_key='xyz' AND meta_value='abc' |
meta_key 有索引,但 meta_key+meta_value 组合无索引 |
|
| 全表扫描评论 | SELECT COUNT(*) FROM wp_comments WHERE comment_approved = '1' |
comment_approved 缺少索引 |
|
插件滥用 WP_Query |
new WP_Query(['post_status'=>'any','posts_per_page'=>-1]) |
加载全部文章导致 JOIN wp_posts+wp_postmeta 全表扫描 |
|
| 主题/插件循环中重复查询 | 在 while(have_posts()) 内调用 get_post_meta() |
N+1 查询问题(100篇文章 → 100次 SQL) |
✅ 步骤 2:实时抓取高消耗查询(无需重启)
-- 查看当前正在执行且耗 CPU 的查询(重点关注 Time > 10s, State='Sending data'/'Sorting result')
SELECT
id, user, host, db, command, time, state,
SUBSTRING(info, 1, 100) as info_short,
ROUND((time*100.0)/(SELECT SUM(time) FROM information_schema.PROCESSLIST WHERE time>0),2) as pct_total_time
FROM information_schema.PROCESSLIST
WHERE time > 5 AND info IS NOT NULL
ORDER BY time DESC LIMIT 10;
-- 查看哪些表被频繁扫描(结合 EXPLAIN)
SHOW OPEN TABLES WHERE In_use > 0;
✅ 步骤 3:检查索引缺失(WordPress 关键表)
-- 检查 wp_posts 表常用查询字段索引
SHOW INDEX FROM wp_posts WHERE Column_name IN ('post_status','post_type','post_date','post_author');
-- 检查 wp_postmeta 组合索引(高频痛点!)
SHOW INDEX FROM wp_postmeta WHERE Column_name IN ('meta_key','meta_value');
-- ✅ 推荐补丁索引(执行前备份!):
ALTER TABLE wp_postmeta ADD INDEX idx_meta_key_value (meta_key, meta_value(191));
ALTER TABLE wp_comments ADD INDEX idx_comment_approved (comment_approved);
ALTER TABLE wp_posts ADD INDEX idx_post_status_type_date (post_status, post_type, post_date);
💡 提示:
meta_value(191)是为兼容 utf8mb4 字符集(WordPress 5.0+ 默认),避免索引过长报错。
🧩 三、排查非 SQL 层面原因(常被忽略!)
| 场景 | 检查命令 | 解决方案 |
|---|---|---|
| PHP-FPM 过度创建 MySQL 连接 | ss -s | grep "mysql" 或 netstat -an | grep :3306 | wc -l |
检查 php-fpm.conf:pm.max_children = 32(根据内存调整)mysql.default_socket = /var/run/mysqld/mysqld.sock(用 socket 替代 TCP) |
| WordPress 对象缓存未启用 | wp cache test(WP-CLI)或检查 wp-config.php 是否定义 WP_CACHE |
启用 Redis/Memcached:define('WP_REDIS_HOST', '127.0.0.1'); + 安装 Redis Object Cache 插件 |
| XML-RPC 暴力攻击 | grep "xmlrpc.php" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -10 |
在 Nginx 配置中禁用:location ~ ^/xmlrpc.php$ { return 403; } |
| 插件冲突(尤其安全/SEO类) | 临时停用所有插件 → 逐个启用测试 | 使用 Query Monitor 插件实时监控每页 SQL 查询数/耗时 |
🚀 四、立竿见影的优化措施(立即生效)
-
Nginx 层缓存静态资源 & 页面
# /etc/nginx/sites-available/your-site location ~ .(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; } # 启用 FastCGI 缓存(对未登录用户) fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS:100m inactive=60m; fastcgi_cache_key "$scheme$request_method$host$request_uri"; -
MySQL 关键参数调优(
/etc/mysql/my.cnf)[mysqld] innodb_buffer_pool_size = 1G # 设为物理内存 50-70%(4GB服务器建议 2G) innodb_log_file_size = 256M # 减少写放大 query_cache_type = 0 # ❌ WordPress 不适用!禁用(MySQL 8.0 已移除) tmp_table_size = 64M max_heap_table_size = 64M table_open_cache = 4000 -
WordPress 层硬核优化
// wp-config.php 中添加(减少数据库交互) define('WP_POST_REVISIONS', 3); // 限制历史版本数 define('AUTOSAVE_INTERVAL', 120); // 自动保存间隔拉长 define('DISABLE_WP_CRON', true); // 禁用内置定时任务,改用系统 cron // 然后添加系统定时任务:`*/15 * * * * cd /var/www/html && wp cron event run --due-now`
📊 五、监控与长期治理
- 部署 Prometheus + Grafana:监控
mysql_global_status_threads_running,mysql_global_status_slow_queries - 每日自动慢日志分析:
# /etc/cron.daily/mysql-slow-report #!/bin/bash pt-query-digest /var/log/mysql/slow.log --since "1 day ago" > /var/log/mysql/slow-report-$(date +%F).log - 使用 Percona Monitoring and Management (PMM):免费开源,专为 MySQL 优化设计,可视化锁等待、索引失效等。
✅ 最终检查清单(部署后验证)
| 项目 | 验证方式 | 合理值 |
|---|---|---|
| 慢查询率 | pt-query-digest 报告 |
< 1% 总查询量 |
| 平均查询响应 | SHOW PROFILES; |
< 200ms(首屏关键查询) |
| 连接数峰值 | mysqladmin status | grep Threads |
< max_connections * 0.8 |
| CPU 使用率 | top 或 htop |
MySQL 持续 < 40%(突发允许 70%) |
💡 经验之谈:
- 80% 的 WordPress MySQL CPU 问题源于
wp_postmeta表缺少组合索引,优先执行ALTER TABLE wp_postmeta ADD INDEX idx_meta_key_value (meta_key, meta_value(191));- 永远不要相信插件作者的“性能优化”宣传——用 Query Monitor 实测每个插件增加的查询数和耗时。
- 升级到 MySQL 8.0+:利用
invisible indexes、descending indexes和更优的优化器(尤其对ORDER BY ... LIMIT查询)。
如需进一步分析,请提供:
① pt-query-digest 报告片段(TOP 5 查询)
② SHOW CREATE TABLE wp_postmeta; 输出
③ mysqltuner.pl 结果(下载地址)
我可为你定制 SQL 优化建议和索引方案。
CLOUD云枢