data_length不能直接代表表真实大小,因其仅含聚簇索引有效数据,不含二级索引等,须与index_length相加才得逻辑总大小;碎片空间data_free在共享表空间下需计入,独立表空间可忽略;table_rows为采样估算值,偏差大;查单库表必须过滤系统库、视图及非持久化引擎表,并用analyze table刷新统计,最终需结合du命令验证物理磁盘占用。

查 information_schema.TABLES 是唯一能从 SQL 层拿到表级空间估算的方式,但必须加过滤、转换单位、且不能当成磁盘真实用量。
为什么不能直接信 DATA_LENGTH?
很多人只取 DATA_LENGTH 字段就以为是“表大小”,其实它只是 InnoDB 聚簇索引中有效数据的近似字节数,完全不含索引——而二级索引、主键 B+ 树非叶子节点、全文索引都算在 INDEX_LENGTH 里。漏掉这部分,低估幅度常达 30%–200%。
-
DATA_LENGTH + INDEX_LENGTH才是逻辑上“数据+索引”的总和,单位是字节 -
DATA_FREE表示已分配但未用的空间(碎片),在共享表空间(innodb_file_per_table=OFF)下必须计入;独立表空间(.ibd文件)下可忽略,文件大小 ≈ 前两者之和 -
TABLE_ROWS是采样估算值,InnoDB 下偏差极大,别用它反推大小
查单库所有表要加哪些必要条件?
不加过滤会混入系统库、视图、临时表,导致结果错乱或权限报错。最简安全写法:
SELECT table_name AS '表名', ROUND((data_length + index_length) / 1024 / 1024, 2) AS '总大小(MB)', ROUND(data_length / 1024 / 1024, 2) AS '数据大小(MB)', ROUND(index_length / 1024 / 1024, 2) AS '索引大小(MB)', table_rows AS '行数' FROM information_schema.tables WHERE table_schema = 'your_db_name' AND table_type = 'BASE TABLE' AND engine IS NOT NULL ORDER BY (data_length + index_length) DESC;
- 必须写
table_type = 'BASE TABLE':排除视图(VIEW)、分区子表、内部元数据表 - 建议加
engine IS NOT NULL:过滤掉 MEMORY、CSV 等无持久化存储的引擎表(它们DATA_LENGTH为 0) - 别漏
table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')—— 如果你查的是全局,不是单库
查单张表时容易踩的性能与精度坑
MySQL 5.7 及更早版本默认不自动更新统计信息,首次查可能看到全 0;InnoDB 表在大量 DELETE/UPDATE 后,DATA_LENGTH 可能变小,但磁盘没释放——这不是查询不准,是引擎特性。
- 执行
ANALYZE TABLE your_table强制刷新统计,再查information_schema.TABLES - 如果
DATA_FREE远大于 0(比如 > 100MB),说明有明显碎片,OPTIMIZE TABLE可能回收空间(注意锁表) - 压缩表(
ROW_FORMAT=COMPRESSED)的DATA_LENGTH是解压后逻辑大小,不是磁盘文件尺寸;真要看 .ibd 文件大小,得去datadir下用ls -lh - 若
innodb_file_per_table=OFF,所有表共用ibdata1,此时DATA_LENGTH完全无法对应物理文件,必须用du -sh /var/lib/mysql/看整体
真正磁盘占用必须用系统命令交叉验证
information_schema.TABLES 的数字永远只是逻辑快照:它不包含 ibdata1(系统表空间)、ibtmp1(临时表空间)、ib_logfile*(redo log)、binlog、slow log,也不反映 purge 滞后或压缩偏差。删库或扩容前,这一步跳不过。
- 看整个 MySQL 数据目录:运行
du -sh /var/lib/mysql(Linux)或du -sh /usr/local/var/mysql(macOS Homebrew) - 看单个库物理目录(仅限
innodb_file_per_table=ON):du -sh /var/lib/mysql/your_db_name - 找最大
.ibd文件:find /var/lib/mysql -name "*.ibd" -exec du -h {} \; | sort -hr | head -10 - 检查是否误把临时文件写进数据目录:
SELECT @@tmpdir;,如果返回路径含/var/lib/mysql,那排序/JOIN 产生的临时文件也会撑大目录
最常被忽略的一点:ibdata1 和 ib_logfile* 是 InnoDB 的核心系统文件,删了 MySQL 直接起不来——别因为看到它们几十 GB 就手抖 rm。











