Nginx + PHP-FPM + MySQL部署WordPress后MySQL CPU占用异常如何诊断?

当 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 等待连接)

🚀 五、立竿见影的优化措施(按优先级)

  1. 紧急止血
    -- 临时终止长期运行查询(ID 来自 PROCESSLIST)
    KILL 12345;
  2. 必做索引(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);
  3. 启用 OPcache + Redis 对象缓存
    • PHP: opcache.enable=1, opcache.memory_consumption=256
    • WordPress: 安装 Redis Object Cache 并启用
  4. 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_keywp_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 » Nginx + PHP-FPM + MySQL部署WordPress后MySQL CPU占用异常如何诊断?