网站打开要等五六秒,你加了缓存、换了 CDN、压了图片,还是慢。
这时候大概率是数据库在拖后腿。WordPress 的每一次页面加载都要查几十上百次数据库,其中只要有一条 SQL 写得不好或者没走索引,整个页面就跟着慢。
这篇文章用一个真实的排查思路,讲清楚怎么找到问题、怎么改。
一、先确认瓶颈是不是在数据库
别上来就查慢日志。先做个快速判断。
看服务器负载
top
- mysqld 长期占 CPU 很高 → 数据库在拼命算,基本确认是它
- %wa(iowait)很高 → 磁盘 I/O 瓶颈,可能是数据库在大量读写临时表
- CPU 都是 php-fpm 占的 → 那问题在 PHP 层面,不是数据库,这篇不适用
装个插件直接看(最简单)
装 Query Monitor 插件,前台打开页面,看顶部工具栏:
- Page Generation Time:总耗时
- Database Queries:查询次数和耗时
- 点进去能看到最慢的那几条 SQL 原文,以及是哪个插件/主题发起的
正常水平:
| 指标 | 健康 | 需要处理 |
|---|---|---|
| 单次页面查询次数 | 30-80 次 | > 200 次 |
| 数据库总耗时 | < 100ms | > 500ms |
| 单条最慢查询 | < 50ms | > 500ms |
如果查询次数超过 300,通常是装了太多插件或者某个插件设计得很差。如果单条查询上秒,那就是索引问题。
二、开启慢查询日志,拿到证据
Query Monitor 只能看当下这个页面,要全面排查还得靠慢查询日志。
开启
在 MySQL 配置文件(/etc/my.cnf 或宝塔里的 MySQL 配置)加:
[mysqld] slow_query_log = 1 slow_query_log_file = /www/server/data/mysql-slow.log long_query_time = 1 # 超过 1 秒的记录下来 log_queries_not_using_indexes = 1
重启 MySQL 生效。
注意:生产环境建议 long_query_time 设 1-2 秒,别设 0.1——日志会爆炸。排查期间可以临时调小,排查完调回来。
看日志
跑一天之后看:
# 看有多少条慢查询 wc -l /www/server/data/mysql-slow.log # 用 mysqldumpslow 汇总(按耗时排序,取前 10) mysqldumpslow -s t -t 10 /www/server/data/mysql-slow.log
或者用更现代的 pt-query-digest(Percona Toolkit 里的工具),输出更详细:
pt-query-digest /www/server/data/mysql-slow.log | head -50
它会把相似的 SQL 归成一类,按总耗时排序。排第一的那个,就是你要优化的目标。
三、看懂执行计划:EXPLAIN
拿到慢 SQL 之后,不要凭感觉改。用 EXPLAIN 让 MySQL 告诉你它打算怎么执行:
EXPLAIN SELECT * FROM wp_posts WHERE post_type = 'product' AND post_status = 'publish';
重点看这几列:
| 列 | 看什么 | 危险信号 |
|---|---|---|
| type | 访问方式 | ALL = 全表扫描,最差。目标是 ref 或 range |
| rows | 预估扫描行数 | 远大于返回行数 = 扫了很多没用的 |
| key | 实际用到的索引 | NULL = 没走索引 |
| Extra | 附加信息 | 出现 Using filesort 或 Using temporary 要注意 |
全表扫描(type=ALL)是最常见的问题:表里 10 万行,它一行行翻过去找你要的 20 行。加个索引就能从几秒变几毫秒。
四、WordPress 里最常见的三类慢查询
1. 查 wp_postmeta(头号问题)
WordPress 把大部分自定义数据都存在 wp_postmeta 表里,这个表又大又容易出问题。
典型慢查询:
SELECT * FROM wp_postmeta WHERE meta_key = '_price' AND meta_value > 100
meta_value 字段默认没有索引。一旦你的文章/产品超过几千条,这类查询就会很慢。
解法:
- 如果业务上必须按 meta_value 查,给它加索引:
ALTER TABLE wp_postmeta ADD INDEX meta_key_value (meta_key, meta_value(50));
(注意
meta_value是 TEXT 类型,必须指定长度) - 但更推荐的做法是:减少这类查询。很多插件在列表页对每篇文章都查一遍 meta(N+1 查询问题),换成一次性批量查,性能提升几十倍
2. 查 wp_options 的 autoload 数据
WordPress 每次加载都会把所有 autoload='yes' 的 options 一次性读出来。这个数据会越来越大——尤其是装了又删的插件会留下垃圾数据。
检查它有多大:
SELECT SUM(LENGTH(option_value)) / 1024 AS size_kb FROM wp_options WHERE autoload = 'yes';
- < 800KB:正常
- > 2MB:明显拖慢每一个页面,必须清理
找出最大的几项:
SELECT option_name, LENGTH(option_value) AS size FROM wp_options WHERE autoload = 'yes' ORDER BY size DESC LIMIT 20;
看到已经卸载的插件留下的 option,删掉(删之前确认插件确实已卸载)。
3. 插件的统计/日志表无限增长
很多统计插件、表单插件、日志插件会往自定义表里塞数据,而且不清理。
看看哪些表最大:
SELECT table_name,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS size_mb,
table_rows
FROM information_schema.tables
WHERE table_schema = '你的数据库名'
ORDER BY size_mb DESC LIMIT 15;
如果发现某个插件的日志表几百 MB 甚至几 GB,而你可能根本用不到那些历史数据——直接在插件设置里清理,或者定期清空。
五、优化的几个层次(从易到难)
层次 1:清理(零风险,立刻见效)
- 清理文章修订版本(revisions):一个几百篇文章的站,修订版可能有几千条
- 清理自动草稿、垃圾评论、回收站内容
- 清理过期的 transient 数据
用 WP-Optimize 或 Advanced Database Cleaner 插件,点几下就完成。操作前先备份数据库。
也可以手动:
DELETE FROM wp_posts WHERE post_type = 'revision'; DELETE FROM wp_postmeta WHERE post_id NOT IN (SELECT ID FROM wp_posts); DELETE FROM wp_options WHERE option_name LIKE '_transient_timeout%' AND option_value < UNIX_TIMESTAMP();
层次 2:加索引(中等风险,效果显著)
针对 EXPLAIN 发现的全表扫描加索引。操作前一定先备份数据库。
常用索引:
ALTER TABLE wp_postmeta ADD INDEX idx_post_meta (post_id, meta_key); ALTER TABLE wp_posts ADD INDEX idx_type_status_date (post_type, post_status, post_date);
层次 3:加缓存(最有效)
前面做的都是「让查询更快」,缓存是「根本不查」。
- 对象缓存(Redis / Memcached):把数据库查询结果缓存起来,同一个查询第二次直接读内存。对动态页面效果最好
- 页面缓存:整个页面生成静态 HTML,完全不碰数据库
宝塔里装 Redis 扩展 + WordPress 装 Redis Object Cache 插件,十分钟的活,数据库查询量通常能降 70% 以上。
层次 4:升级硬件或迁移数据库
如果数据量真的很大(比如 WooCommerce 几万订单),前面都做了还是慢,考虑:
- 把数据库独立到单独一台机器
- 升级到 SSD(机械硬盘跑数据库是灾难)
- 增加
innodb_buffer_pool_size(一般是物理内存的 60-70%)
六、一个关键配置:innodb_buffer_pool_size
这是 InnoDB 引擎的缓存池大小,对性能影响最大的单个参数。默认可能只有 128M,对独立站太小了。
查看当前值:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
建议设置:
- 2G 内存服务器:512M – 1G
- 4G 内存服务器:2G
- 8G 以上:物理内存的 60-70%
在 my.cnf 里改,重启 MySQL。
最后
数据库优化有个特点:往往 1-2 条 SQL 就吃掉 80% 的时间。所以不要试图优化所有查询——找到最慢的那几条,改掉,效果立竿见影。
而且对大多数独立站来说,加 Redis 对象缓存 + 清理 wp_options 垃圾这两件事做完,数据库基本就不会是瓶颈了。不需要成为 DBA。