一个内容站跑到三千篇文章之后,通常会开始出现这些症状:后台列表要转三秒、编辑文章保存慢、首页偶尔超时。多数人第一反应是"服务器不行了",其实是数据库该整理了。
一、膨胀的四个原因
1. 修订版本堆积
WordPress 每保存一次就存一个修订版本,一篇文章改十次就有十个副本。几千篇文章下来,postmeta 表能到几百万行。
2. 垃圾数据
回收站里的文章、被删除但仍残留的关联记录、失败的草稿、插件留下的临时数据。
3. 缺少索引
常见的查询是"按栏目 + 状态 + 时间排序",如果这三个字段没有合适的复合索引,每次都要全表扫描。
4. 表碎片
频繁删除插入会产生碎片,表文件变大但实际数据没那么多,查询时需要读更多页。
二、五条优化手段
1. 清理修订版本与垃圾数据
-- WordPress 示例:删除修订版本
DELETE FROM wp_posts WHERE post_type = 'revision';
-- 清理孤立 postmeta
DELETE pm FROM wp_postmeta pm
LEFT JOIN wp_posts p ON p.ID = pm.post_id
WHERE p.ID IS NULL;
执行前必须备份。数据量大时分批删,一次一万行,避免锁表。
2. 加复合索引
ALTER TABLE articles
ADD INDEX idx_list (category_id, status, published_at);
索引要按查询条件的顺序建:等值条件在前,范围/排序在后。建完用 EXPLAIN 看是否命中。
3. 优化表
OPTIMIZE TABLE articles, article_tags;
回收碎片空间。大表执行时会锁表,放在凌晨做。
4. 开启查询缓存或对象缓存
MySQL 8.0 已移除查询缓存,改用 Redis 做对象缓存。把栏目、热门文章、配置项缓存起来,能减少大量重复查询。
5. 分离冷热数据
超过两年的文章归档到独立表或独立库,主表只留近期内容。列表查询会快很多。
三、不写代码也能做的清理
- 后台清空回收站。
- 删除未使用的标签和空栏目。
- 卸载不用的插件,并清理其遗留表。
- 关闭文章的自动保存草稿,或调低保存频率。
- 图片附件删除后,同步清理数据库里的记录。
四、怎么定位慢查询
开慢查询日志,阈值设 1 秒:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
跑一天后看日志,找出执行次数多、耗时长的 SQL。重点看两类:没有索引的全表扫描、以及 ORDER BY RAND() 这类随机排序(后台"随机推荐"常用,非常耗性能)。
五、优化后的验证
三个指标:后台列表加载时间、首页首字节时间、MySQL 的 QPS 和慢查询数量。优化前后各测一次,用数据说话。
如果优化后仍然慢,问题可能在程序层:比如列表页每篇文章都单独查一次标签(N+1 查询),这种情况加索引也没用,要改代码。
先拆大字段,再谈分表
列表查询带着正文,是文章表变慢最常见的原因。把正文拆到独立表,列表只查标题、栏目、时间;再给 site_id、status、published_at 建联合索引。做完这两步,十万篇也能跑。
分表是最后的手段,不是第一选择。大多数站点的问题在查询方式,不在数据量。
正文表单独放,列表查询只取标题、栏目和时间。把这一步做好,比任何分表方案都有效。
常见问题
优化表会不会丢数据
不会,但会锁表。大表建议用 pt-online-schema-change 之类的工具做在线变更。
要不要换更强的服务器
先优化再看。多数"性能问题"优化后能撑住,换服务器只是把问题推迟。
多少篇文章需要开始优化
三千篇是个信号,五千篇之后必须处理。具体看查询复杂度,不是绝对数字。
数据库之外,整体架构的设计见CMS选型指南。