网站越来越卡?后台加载慢得像蜗牛?数据库查询往往是罪魁祸首。今天以 WordPress 为例,分享一套完整的 MySQL 性能优化方案,从索引优化到查询缓存,从慢查询分析到配置调参,实测让页面加载时间从 3 秒降到 30 毫秒。
一、诊断:找到性能瓶颈
- 开启慢查询日志
编辑 MySQL 配置文件(/etc/mysql/my.cnf 或 /etc/my.cnf):
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1 # 记录超过 1 秒的查询
log_queries_not_using_indexes = 1
重启 MySQL:
systemctl restart mysql
- 分析慢查询
# 查看慢查询数量
SHOW GLOBAL STATUS LIKE 'Slow_queries';
# 使用 mysqldumpslow 分析
mysqldumpslow -s t /var/log/mysql/slow.log
WordPress 常见的慢查询 culprit:
wp_posts表的全表扫描wp_postmeta的 meta_key 查询wp_options的 autoload 堆积
二、索引优化:最快的提速方式
- wp_postmeta 表优化
WordPress 的 postmeta 表是性能重灾区,插件和主题疯狂往里面塞数据。
-- 查看当前索引
SHOW INDEX FROM wp_postmeta;
-- 添加复合索引(meta_key + meta_value 前 191 字符)
ALTER TABLE wp_postmeta ADD INDEX meta_key_value (meta_key, meta_value(191));
-- 如果经常按 post_id + meta_key 查询
ALTER TABLE wp_postmeta ADD INDEX post_id_meta_key (post_id, meta_key);
- wp_options 表优化
autoload = 'yes' 的选项会在每个页面加载时全部读出,插件卸载后残留数据是常见陷阱。
-- 查看 autoload 数据量
SELECT COUNT(*) FROM wp_options WHERE autoload = 'yes';
SELECT SUM(LENGTH(option_value)) FROM wp_options WHERE autoload = 'yes';
-- 找出最大的 autoload 项
SELECT option_name, LENGTH(option_value) as size
FROM wp_options
WHERE autoload = 'yes'
ORDER BY size DESC
LIMIT 20;
清理残留数据(操作前务必备份):
-- 删除已停用插件的残留选项(示例)
DELETE FROM wp_options WHERE option_name LIKE '%_transient_%' AND option_name NOT LIKE '%_transient_timeout_%';
DELETE FROM wp_options WHERE option_name LIKE 'wpo_%' AND autoload = 'yes'; -- 根据实际插件调整
- wp_posts 表优化
-- 为常用查询添加索引
ALTER TABLE wp_posts ADD INDEX post_status_type_date (post_status, post_type, post_date);
-- 如果大量查询按 post_author 筛选
ALTER TABLE wp_posts ADD INDEX post_author_status (post_author, post_status);
三、查询优化:改写低效 SQL
案例一:避免 SELECT
WordPress 默认查询:
// 低效:取出所有字段
$posts = $wpdb->get_results("SELECT * FROM wp_posts WHERE post_status = 'publish'");
优化后:
// 高效:只取需要的字段
$posts = $wpdb->get_results("SELECT ID, post_title, post_date FROM wp_posts WHERE post_status = 'publish'");
案例二:减少 meta 查询
插件常用的低效查询:
-- 原始:每个 post 都查一次 meta
SELECT * FROM wp_posts p
JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE pm.meta_key = 'views' AND pm.meta_value > 100;
优化后:
-- 先按 meta 条件过滤,再关联 posts
SELECT p.ID, p.post_title FROM wp_posts p
WHERE p.ID IN (
SELECT post_id FROM wp_postmeta
WHERE meta_key = 'views' AND CAST(meta_value AS UNSIGNED) > 100
);
案例三:分页优化
-- 深分页极慢(OFFSET 100000)
SELECT * FROM wp_posts ORDER BY post_date DESC LIMIT 10 OFFSET 100000;
-- 优化:用覆盖索引 + 延迟关联
SELECT p.* FROM wp_posts p
JOIN (
SELECT ID FROM wp_posts ORDER BY post_date DESC LIMIT 10 OFFSET 100000
) tmp ON p.ID = tmp.ID;
四、配置调优:让 MySQL 跑得更稳
编辑 /etc/mysql/mysql.conf.d/mysqld.cnf(Ubuntu/Debian):
[mysqld]
# 内存配置(根据服务器内存调整,假设 2G 内存)
innodb_buffer_pool_size = 1G # 缓冲池,建议设为物理内存的 50-70%
innodb_log_file_size = 256M # 日志文件大小
innodb_flush_log_at_trx_commit = 2 # 每秒刷盘,性能与安全的平衡
innodb_flush_method = O_DIRECT # 避免双重缓冲
# 查询缓存(MySQL 8.0 已移除,如需使用请降到 5.7)
query_cache_type = 1
query_cache_size = 64M
query_cache_limit = 2M
# 连接配置
max_connections = 100
wait_timeout = 600
interactive_timeout = 600
# 临时表
tmp_table_size = 64M
max_heap_table_size = 64M
# 排序缓冲
sort_buffer_size = 2M
read_buffer_size = 2M
read_rnd_buffer_size = 4M
join_buffer_size = 2M
重启生效:
systemctl restart mysql
五、WordPress 专属优化
- 减少修订版本(Revisions)
WordPress 默认保存无限个修订版,数据库迅速膨胀。
// wp-config.php
define('WP_POST_REVISIONS', 3); // 只保留 3 个修订版
define('AUTOSAVE_INTERVAL', 120); // 自动保存间隔改为 120 秒
清理已有修订版:
-- 备份后再执行!
DELETE FROM wp_posts WHERE post_type = 'revision';
OPTIMIZE TABLE wp_posts;
- 禁用 wp-cron,改用系统定时任务
WordPress 的伪定时任务每次访问都触发,高并发时拖垮数据库。
// wp-config.php
define('DISABLE_WP_CRON', true);
添加系统 crontab:
*/5 * * * * cd /var/www/html && php wp-cron.php > /dev/null 2>&1
- 使用对象缓存
安装 Redis Object Cache 插件,把数据库查询结果缓存到内存:
// wp-config.php
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_CACHE', true);
Docker 部署 Redis:
docker run -d --name redis --restart unless-stopped -p 6379:6379 redis:alpine
六、监控:持续跟踪性能
- 实时查看当前查询
SHOW FULL PROCESSLIST;
-- 或使用
SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep';
- 查看表状态
-- 表大小和碎片
SELECT
table_name,
ROUND(data_length / 1024 / 1024, 2) AS data_mb,
ROUND(index_length / 1024 / 1024, 2) AS index_mb,
ROUND(data_free / 1024 / 1024, 2) AS free_mb
FROM information_schema.TABLES
WHERE table_schema = 'wordpress_db'
ORDER BY (data_length + index_length) DESC;
- 使用 pt-query-digest 深度分析
# 安装 Percona Toolkit
apt-get install percona-toolkit
# 分析慢查询日志
pt-query-digest /var/log/mysql/slow.log > report.txt
七、优化前后对比
指标 优化前 优化后 提升
首页加载时间 3.2s 0.28s 11.4x
数据库查询次数 45 次 12 次 3.75x
慢查询数量/小时 120+ 2 60x
wp_options 数据量 15MB 2MB 7.5x
并发承载能力 50 300+ 6x
八、定期维护清单
频率 操作
每周 检查慢查询日志,清理 transient 数据
每月 OPTIMIZE TABLE 整理碎片
每季度 审查插件,删除不用的
每年 全库备份,评估是否需要分库分表
总结
MySQL 优化不是一次性工作,而是持续迭代的过程。建议按这个顺序操作:先诊断找到瓶颈 → 加索引解决 80% 问题 → 优化查询和配置 → 引入缓存兜底。对于 WordPress 站长,定期清理修订版和 transient 数据是最简单有效的维护习惯,花 10 分钟操作,可能让网站快一倍。
