information_schema.TABLES 是唯一能直接查表级空间占用的系统视图,但 DATA_FREE 在独立表空间中恒为0或极小,仅共享表空间下有效;TABLE_ROWS 为估算值,与真实磁盘占用无关;真实空间需结合 .ibd 文件大小、INDEX_LENGTH 和 COUNT(*) 综合判断。

information_schema.TABLES 是唯一能直接查出表级空间占用的系统视图,但它返回的是估算值,不是磁盘真实使用量。你得结合字段含义、存储引擎行为和实际文件状态一起看,否则容易误判“空间快满了”或“该删数据了”。
为什么 DATA_FREE 在 InnoDB 独立表空间里基本没用
当 innodb_file_per_table = ON(MySQL 5.6+ 默认开启),每张 InnoDB 表对应一个 .ibd 文件,此时 DATA_FREE 值恒为 0 或极小——因为文件系统层面不维护“空闲页池”,空间不会归还给表空间,只在文件内部复用。
-
DATA_LENGTH + INDEX_LENGTH≈.ibd文件大小(单位字节),可用ls -lh /var/lib/mysql/your_db/your_table.ibd验证 - 如果
DATA_FREE显著大于 0,大概率说明表还在共享表空间(ibdata1)里,或者innodb_file_per_table被关掉了 - 别拿
DATA_FREE / (DATA_LENGTH + INDEX_LENGTH)算“剩余率”——对独立表空间这个比值无意义
TABLE_ROWS 和空间估算完全不挂钩
TABLE_ROWS 是优化器用的统计值,InnoDB 下是采样估算,误差常达 ±50%,且不更新行头开销、变长字段实际长度、事务系统段等真实磁盘成本。用它反推单行平均大小会严重失真。
- 大表想看真实行均大小?执行
SELECT AVG(LENGTH(CONCAT_WS('', col1, col2, ...))) FROM your_table(注意 NULL 处理) - 但更准的做法是:取
(DATA_LENGTH + INDEX_LENGTH) / 实际行数,而实际行数必须用COUNT(*)——不过千万慎用于十亿级表 - 视图(
table_type = 'VIEW')的DATA_LENGTH恒为 0,混入查询会拉低总和,务必加AND table_type = 'BASE TABLE'
计算库级空间使用率时,DATA_FREE 只对共享表空间有效
如果你的实例仍用共享表空间(innodb_file_per_table = OFF),所有 InnoDB 表共用 ibdata1,这时 DATA_FREE 才反映全局未分配页数。但注意:它不包含 undo log、change buffer、doublewrite buffer 等区域的占用。
- 安全的库级使用率公式是:
ROUND(SUM(DATA_LENGTH + INDEX_LENGTH - DATA_FREE) / SUM(DATA_LENGTH + INDEX_LENGTH) * 100, 2) - 但仅当所有表都是 InnoDB 且共享表空间时才成立;混合引擎(比如有 MyISAM 表)会让分母失真
- 真正要监控磁盘爆满风险?盯死
df -h和ibdata1文件增长趋势,而不是依赖information_schema的百分比
查大表空间分布,别漏掉 INDEX_LENGTH
很多人只看 DATA_LENGTH,结果发现一张表“数据才 200MB,怎么磁盘占了 1.2GB?”——答案往往在二级索引、全文索引或冗余联合索引上。
- 执行
SELECT TABLE_NAME, ROUND(DATA_LENGTH/1024/1024, 2), ROUND(INDEX_LENGTH/1024/1024, 2) FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db' ORDER BY INDEX_LENGTH DESC LIMIT 5 - 如果
INDEX_LENGTH > DATA_LENGTH,立刻检查SHOW CREATE TABLE,看有没有没用的UNIQUE索引或过宽的VARCHAR前缀 - 压缩表(
ROW_FORMAT=COMPRESSED)的DATA_LENGTH是解压后逻辑大小,.ibd文件可能只有它的 1/3,这时必须看文件本身
真正难的不是算出一个百分比,而是判断这个数字背后是否藏着可优化的空间。比如 DATA_FREE 很高,不一定代表能 OPTIMIZE;INDEX_LENGTH 突增,未必是业务增长,可能是开发误加了索引。所有数值都得回到具体表、具体引擎、具体配置里交叉验证。


















