mysql表空间碎片主要通过导致information_schema.innodb_tablestats统计失真,使优化器选错执行计划(如该走索引却全表扫描),从而降低查询速度;点查几乎不受影响,范围扫描、排序等场景受损明显。

MySQL 表空间碎片怎么影响查询速度
磁盘碎片本身不直接拖慢查询,真正掉性能的是 INFORMATION_SCHEMA.INNODB_TABLESTATS 里 avg_row_length 和 data_length 严重失真,导致优化器选错执行计划——比如该走索引却全表扫描,或者误判 join 顺序。
常见现象:同一 SQL 在不同时间执行,EXPLAIN 显示的 rows 值波动极大(从几百跳到几十万),但表数据量没变;SELECT COUNT(*) 突然变慢;ORDER BY ... LIMIT 响应延迟明显升高。
- InnoDB 的“碎片”本质是页内空闲空间 + 高水位线后未回收的页,不是文件系统那种连续性问题
-
OPTIMIZE TABLE会重建整张表,释放空闲页并重排聚簇索引,但期间锁表(5.7+ 对普通表是 online DDL,但仍有短时 X 锁) - 碎片对点查(
WHERE pk = ?)几乎无影响,主要伤在范围扫描、排序、临时表创建等需要遍历大量页的场景
什么时候必须做 OPTIMIZE TABLE
别看 DATA_FREE 字段值大就急着优化。真正该动的时候,是满足以下至少一项:
-
SHOW TABLE STATUS LIKE 't1'返回的Data_free>Data_length× 0.25(即空闲空间超实际数据量 1/4) -
SELECT (data_length + index_length) / table_rows AS avg_row_size FROM INFORMATION_SCHEMA.TABLES结果远大于你预估的单行大小(比如算出来 2KB,但实际只有 200B) - 频繁
DELETE大量行后没做过任何整理,且后续写入以INSERT为主(旧页无法复用,新页不断分配)
注意:OPTIMIZE TABLE 对 ROW_FORMAT=COMPRESSED 表可能失败;MySQL 8.0.29+ 默认关闭 innodb_file_per_table 时,它对系统表空间里的表无效。
替代方案:ALTER TABLE ... FORCE 更轻量
如果只是想触发页合并、更新统计信息,又不想承受 OPTIMIZE TABLE 的锁开销,ALTER TABLE t1 ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE 太重,而 ALTER TABLE t1 FORCE 是更干净的选择。
- 它本质是重建表,但语法更短,语义更明确,MySQL 会自动选最优 ALGORITHM
- 在 5.6+ 中,只要没改列定义,它默认走 online DDL,只在最后短暂加 MDL 写锁
- 比
OPTIMIZE TABLE更可控:你可以加WAIT 30防止被长事务阻塞太久 - 执行后务必检查
SHOW ENGINE INNODB STATUS\G里的 “ROW OPERATIONS” 是否有异常重试
自动化清理前必须确认的三件事
脚本定时跑 OPTIMIZE TABLE 或 ALTER TABLE ... FORCE 很危险,先人工验证这三点:
- 确认表没被长事务占用:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 300 - 检查
innodb_buffer_pool_pages_dirty是否持续高位,避免优化过程中刷脏页压力爆炸 - 验证备份链完整:
OPTIMIZE后 binlog position 会跳变,如果依赖物理备份 + binlog 恢复,需确保 backup 工具已记录新起始点
碎片不是越小越好——过度优化反而增加 I/O 和 buffer pool 压力。真正要盯的是统计信息准确性,而不是 Data_free 数字本身。











