MySQL数据库碎片清理,优化大表性能

MySQL 数据库碎片清理是优化大表性能的常见手段,尤其当表频繁增删改后,数据页出现空洞、索引统计信息不准,查询会明显变慢。
本文面向零基础用户,讲清碎片产生原因,并给出可执行的清理步骤、避坑要点和验证方法。

先判断你的表是否真的需要清理

碎片不是凭空出现的。
InnoDB 存储引擎在删除或更新变长字段时,不会立即回收空间,而是留下空洞。
这些空洞会让表占用的磁盘空间比实际数据大,扫描时读取更多页,拖慢查询。

不是所有表都需要清理。只有数据频繁删除、更新,且表体积较大时,碎片影响才明显
小表清理收益很低,反而可能锁表。

检查碎片情况,可以查询 information_schema.TABLES

SELECT
  TABLE_NAME,
  ROUND(DATA_LENGTH / 1024 / 1024, 2) AS data_mb,
  ROUND(DATA_FREE / 1024 / 1024, 2) AS free_mb,
  ROUND(DATA_FREE / (DATA_LENGTH + INDEX_LENGTH + DATA_FREE) * 100, 2) AS frag_pct
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = '你的数据库名'
  AND DATA_FREE > 0
ORDER BY DATA_FREE DESC;

DATA_FREE 表示碎片占用的字节数。
如果 frag_pct 超过 20%,且表大小在几百 MB 以上,清理通常有明显收益。

清理前的准备:备份、选时间、确认引擎

动手前必须做三件事:

  1. 备份数据。至少用 mysqldump 导出目标表:
   mysqldump -u root -p 数据库名 表名 > /backup/表名_$(date +%F).sql

大表建议用 --single-transaction 减少锁:

   mysqldump -u root -p --single-transaction 数据库名 表名 > /backup/表名.sql
  1. 选择业务低峰期。清理操作可能锁表,尤其是 MyISAM 引擎,InnoDB 在 MySQL 5.6 之后支持 Online DDL,但仍会消耗 IO。
  2. 确认存储引擎。执行 SHOW TABLE STATUS LIKE '表名';,看 Engine 列。MyISAM 碎片清理简单但锁全表;InnoDB 推荐用 ALTER TABLE ... ENGINE=InnoDB 重建。
宝塔面板用户可以在“数据库”页面点击“管理”,进入 phpMyAdmin 后选择表,点击“操作”标签,找到“优化表”按钮。但大表不建议在 phpMyAdmin 中操作,容易超时。

执行碎片清理:三种方法按场景选

方法一:OPTIMIZE TABLE(适合小表或 MyISAM)

OPTIMIZE TABLE 表名;

这个命令会重建表并释放空间。
对 InnoDB 表,MySQL 会将其映射为 ALTER TABLE ... FORCE,触发重建。
注意:执行期间会占用额外磁盘空间,需要至少和原表一样大的剩余空间

如果表很大,可能报错 ERROR 1114 (HY000): The table '表名' is full,说明临时目录空间不足。
可以修改 tmpdir 到更大分区,或改用方法二。

方法二:ALTER TABLE 重建(InnoDB 大表推荐)

ALTER TABLE 表名 ENGINE=InnoDB;

这会重建表并整理碎片。
MySQL 5.6 及以上支持 Online DDL,允许并发读写,但需要关注 innodb_online_alter_log_max_size 参数,默认 128M,大表操作可能超出导致失败。
可以临时调大:

SET GLOBAL innodb_online_alter_log_max_size = 512 * 1024 * 1024;

执行后观察是否完成,不要中途 kill,否则可能留下临时表。

方法三:pt-online-schema-change(超大型表、不能停业务)

Percona Toolkit 的工具,通过创建影子表、复制数据、原子重命名完成,几乎不锁原表。
安装后执行:

pt-online-schema-change --alter "ENGINE=InnoDB" \
  --host=127.0.0.1 --user=root --ask-pass \
  D=数据库名,t=表名 --execute

执行前会检查外键、触发器,建议先加 --dry-run 试运行。注意:磁盘空间要足够容纳新旧两张表

避坑指南:这些情况别乱清理

  • 主从复制环境:清理操作会写入 binlog,从库可能延迟。建议在从库先测试,或设置 sql_log_bin=0 临时跳过(需谨慎,可能导致主从不一致)。
  • 磁盘空间不足:重建表需要额外空间,至少预留原表大小 1.2 倍。
  • 外键约束ALTER TABLE 可能因外键失败,先用 SET FOREIGN_KEY_CHECKS=0; 临时关闭,完成后恢复。
  • 业务高峰期:即使 Online DDL 也会消耗 CPU 和 IO,可能拖慢其他查询。
  • MyISAM 表OPTIMIZE TABLE 会锁全表,期间无法读写,务必在停写窗口操作。

如果清理后碎片比例仍然很高,可能是表本身写入模式导致,比如频繁更新变长字段。
这种情况可以调整表结构,把频繁更新的字段拆分到单独表。

清理后怎么验证效果

重新执行第一步的碎片查询,对比 DATA_FREEfrag_pct
理想情况下 DATA_FREE 应接近 0,表文件大小也会下降。

再用 EXPLAIN 观察典型查询的执行计划:

EXPLAIN SELECT * FROM 表名 WHERE 索引列 = '值';

关注 rows 列是否减少,type 是否从 ALL 变为 refrange
碎片清理不会改变索引结构,但重建后统计信息更新,优化器可能选择更优路径。

也可以对比清理前后的查询耗时:

SELECT BENCHMARK(1000000, (SELECT COUNT(*) FROM 表名 WHERE 条件));

BENCHMARK 只适合粗略对比,生产环境建议用慢查询日志观察实际业务 SQL。

常见疑问

清理碎片会丢失数据吗?
正常操作不会。但任何重建操作都有风险,务必先备份。如果中途断电或磁盘满,可能损坏表,需要从备份恢复。

多久清理一次合适?
没有固定周期。建议每月检查一次 DATA_FREE,当碎片比例超过 20% 且影响查询时再清理。频繁清理反而增加 IO 负担。

OPTIMIZE TABLE 和 ALTER TABLE 有什么区别?
对 InnoDB,两者最终都触发重建,但 OPTIMIZE TABLE 会返回信息行,ALTER TABLE 更直接。大表推荐 ALTER TABLEpt-online-schema-change

清理后表大小没变?
可能因为 innodb_file_per_table 关闭,表空间共享,文件不会缩小。可以检查该参数,如果为 OFF,需要先开启并重建表才能释放空间。

MySQL 数据库碎片清理不是万能药,但针对频繁增删改的大表,定期维护能明显改善查询性能。
操作前备份、选低峰、留足空间,清理后验证碎片比例和执行计划,才算完整闭环。

分享到:
上一篇
MySQL设置定时自动备份,mysqldump脚本
下一篇
MySQL用户权限管理,禁止root远程登录
1
系统公告

机房迁移升级通知

尊敬的用户: IP 段 103.23.148.x、156.224.29.x 原香港一区线路波动、攻击频繁,平台定于 7 月 5 日凌晨分批迁移至香港 GIA 机房,硬件升级 AMD 铂金机型。 迁移均在凌晨操作,最大程度降低业务影响,迁移期间服务器临时关机; 升级后配置不降低、费用不涨价,数据默认同步迁移; 迁移后 IP 全部更换,请及时修改域名解析、防火墙白名单; 建议提前备份重要数据,有问题可联系在线客服。 感谢理解与支持! 泽御云科技 2026.06.30
服务中心
客服
在线客服
24小时为您服务
咨询
联系我们
联系我们,为您的业务提供专属服务。
24/7 技术支持
如果您遇到寻求进一步的帮助,请过工单与我们进行联系。
24/7 即时支持
泽御云
售前客服
泽御云
泽御云
售后客服
泽御云
技术支持
评价
您对当前页面的整体感受是否满意?
😞
非常不满意
😕
不满意
😐
一般
🙂
满意
😊
非常满意