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

当 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 查询数/耗时

🚀 四、立竿见影的优化措施(立即生效)

  1. 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";
  2. 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
  3. 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 使用率 tophtop 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 indexesdescending indexes 和更优的优化器(尤其对 ORDER BY ... LIMIT 查询)。

如需进一步分析,请提供:
pt-query-digest 报告片段(TOP 5 查询)
SHOW CREATE TABLE wp_postmeta; 输出
mysqltuner.pl 结果(下载地址)

我可为你定制 SQL 优化建议和索引方案。

未经允许不得转载:CLOUD云枢 » Nginx + PHP-FPM + MySQL部署WordPress后MySQL CPU占用异常如何诊断?