本文约 3,186 字,预计阅读 8 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:数据库莫名涨到 800MB
有个很典型的现象:站点文章才 300 篇,但 wp_posts 表导出后有 700MB,wp_postmeta 更夸张。后台文章列表加载要七八秒。
原因几乎总是同一个:修订版本(revision)。
一篇反复修改的文章,每次点”更新”或每次自动保存,都会往 wp_posts 里插一条 post_type = revision 的记录,同时在 wp_postmeta 里复制一份它当时所有的自定义字段。改 50 次就是 50 条记录 + 几百行 meta。
原理分析:修订版本和自动保存不是一回事
这两个经常被混为一谈,其实机制不同:
| 项目 | 修订版本 revision | 自动保存 autosave |
|---|---|---|
| 触发 | 每次点”更新/发布” | 编辑时每隔 60 秒自动触发 |
| 存储 | 每次新建一条记录 | 浏览器 localStorage + 一条覆盖式记录 |
post_type |
revision |
revision(post_name 带 -autosave-v1) |
| 数量 | 无限增长 | 每篇文章只保留 1 条最新的 |
自动保存的那条记录会覆盖上一次的,不会无限膨胀。真正撑爆数据库的是修订版本。
另外,wp_postmeta 的膨胀比 wp_posts 更严重:某些页面构建器(Elementor、WPBakery)会给每个修订版本存一份完整的布局数据。所以你看到的”数据库大”,八成是 meta 表。
保姆级实操:三步处理
第一步:先看看有多少
SELECT COUNT(*) AS rev_count, SUM(LENGTH(post_content)) AS content_bytes
FROM wp_posts WHERE post_type = 'revision';
或者用 WP-CLI 直接看:
wp post list --post_type=revision --fields=ID,post_parent,post_date --format=count
按文章统计修订数最多的几篇:
SELECT post_parent, COUNT(*) AS cnt
FROM wp_posts
WHERE post_type = 'revision'
GROUP BY post_parent
ORDER BY cnt DESC
LIMIT 20;
第二步:限制未来的产生量(改 wp-config.php)
完全关闭修订(不推荐,因为丢了后悔药):
define( 'WP_POST_REVISIONS', false );
限制每篇最多保留 N 个(推荐):
define( 'WP_POST_REVISIONS', 5 );
define( 'AUTOSAVE_INTERVAL', 120 ); // 自动保存间隔,单位秒
注意:WP_POST_REVISIONS 必须在 /* That's all, stop editing! */ 这一行之前定义,写在文件末尾无效。这是最常见的失误。
只对某些文章类型关闭:
add_filter( 'wp_revisions_to_keep', function( $num, $post ) {
if ( 'product' === $post->post_type ) {
return 0; // 产品不保留修订
}
return 5;
}, 10, 2 );
第三步:清理已经产生的历史修订
推荐 WP-CLI,最安全:
# 先看看会删多少(实际不删)
wp post delete $(wp post list --post_type=revision --format=ids) --dry-run
# 确认后执行
wp post delete $(wp post list --post_type=revision --format=ids) --force
数据量太大时命令行参数会超长,用循环:
wp post list --post_type=revision --format=ids --posts_per_page=500 > /tmp/ids.txt
while read -r ids; do
[ -z "$ids" ] && continue
wp post delete $ids --force --quiet
done < /tmp/ids.txt
纯 SQL 版本(更快,但务必先备份):
-- 先备份
-- wp db export before-revision-clean.sql
DELETE p, pm
FROM wp_posts p
LEFT JOIN wp_postmeta pm ON pm.post_id = p.ID
WHERE p.post_type = 'revision';
这条语句用 DELETE p, pm 的多表删除语法,一次性清掉修订记录和它关联的 meta。只删 wp_posts 不删 meta 的话,wp_postmeta 会留下大量孤儿行,等于白清理一半。
清理后优化表释放空间:
OPTIMIZE TABLE wp_posts;
OPTIMIZE TABLE wp_postmeta;
InnoDB 的
OPTIMIZE TABLE会重建表并锁表,大表执行时间可能几分钟,一定要在业务低峰做,且确保磁盘剩余空间大于表大小。开启innodb_file_per_table(MySQL 5.6+ 默认开启)才会真正把空间还给文件系统。
常见踩坑与失败案例
define写错位置:写在wp-config.php末尾、require_once(ABSPATH . 'wp-settings.php')之后,常量不生效,白改。- 案例:某站在
functions.php里用remove_post_type_support( 'post', 'revisions' )想关闭修订——这个只对新建文章类型有效,对已有的post无效,因为他是在init之后才执行的。正确方式是wp-config.php定义常量,或用wp_revisions_to_keep过滤器。 - 误删 autosave 导致正在编辑的内容丢失:清理时如果有编辑正在写文章,他的 autosave 被删,浏览器恢复提示会失效。深夜清理最稳妥。
- 用了修订版本做协作审核:有些团队靠 revision 追溯谁改了什么,一刀切关掉会丢能力。这种情况建议设成 10~20 而不是 false。
- WooCommerce / 自定义文章类型:
WP_POST_REVISIONS对所有支持 revision 的类型生效,产品、页面也一样。如果只针对文章,用过滤器判断post_type。 - 清理后后台还是慢:那问题不在修订,去查
wp_options里的_transient_垃圾和插件自己建的表(如 wp_wfHits、wp_social 统计表)。
总结
处理修订版本的顺序是:先量化 → 再用 wp-config.php 里 WP_POST_REVISIONS 常量限制增量(写在正确位置)→ 最后用 WP-CLI 或双表 DELETE 清存量 → OPTIMIZE TABLE 在低峰期释放空间。日常建议保留 5~10 个修订,既留了后悔药,又不至于让数据库失控。如果站点是 Elementor 之类构建器做的,效果会特别明显,往往一清理就是几百 MB。
