information_schema.tables 的 data_length 和 index_length 是 innodb/btree 层面的页分配估算值,并非磁盘真实占用;真正磁盘占用需通过文件系统命令如 du 查看,二者存在 text 外部存储、共享表空间、undo log 等导致的差异。

information_schema.TABLES 的 data_length 和 index_length 不是磁盘真实占用
information_schema.TABLES 里的 DATA_LENGTH 和 INDEX_LENGTH 是 InnoDB/BTree 层面的页分配估算值,不是操作系统看到的文件大小。比如:
- TEXT/BLOB 字段启用外部存储时,这部分实际可能存于独立的
ibd文件或共享表空间,但DATA_LENGTH可能只计指针开销 - 启用了
innodb_file_per_table=OFF,所有表数据都挤在ibdata1里,DATA_LENGTH汇总后远小于ibdata1文件本身 - 临时表、undo log、change buffer 等不暴露给
TABLES视图,完全不计入
用 SQL 快速查各库“估算容量”要绕过 NULL 和系统库
直接对 information_schema.TABLES 按 TABLE_SCHEMA 分组求和,会把 mysql、performance_schema、information_schema 这些系统库也卷进来,且部分行的 DATA_LENGTH 为 NULL —— 导致 SUM() 返回 NULL。
- 必须加
WHERE TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema') - 必须用
IFNULL(DATA_LENGTH, 0)替换空值,否则整组求和失败 - 单位换算建议统一除以
1024/1024再ROUND(..., 2),避免浮点误差累积
示例语句:
SELECT
TABLE_SCHEMA AS `数据库`,
ROUND(SUM(IFNULL(DATA_LENGTH, 0)) / 1024 / 1024, 2) AS `数据大小(MB)`,
ROUND(SUM(IFNULL(INDEX_LENGTH, 0)) / 1024 / 1024, 2) AS `索引大小(MB)`
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema')
GROUP BY TABLE_SCHEMA
ORDER BY (IFNULL(DATA_LENGTH, 0) + IFNULL(INDEX_LENGTH, 0)) DESC;
真正看磁盘占用得进文件系统,不是靠 SQL
MySQL 自身不扫描磁盘,也不维护 OS 级文件大小缓存。最准的方式是进 Linux 执行 du,前提是知道 @@datadir 路径且有文件系统权限。
- 先查路径:
mysql -Nse "SELECT @@datadir;" - 再统计(仅限
innodb_file_per_table=ON场景):du -sh /var/lib/mysql/[a-zA-Z0-9]* 2>/dev/null | sort -hr - 若
innodb_file_per_table=OFF,ibdata1占用无法按库拆分,只能看整个datadir目录总量 - 注意:MySQL 进程用户可能无权读取某些目录(如被
chown锁定),du报错需切root或调整权限
监控场景别每次实时算,要定期采集
频繁执行 SELECT ... FROM information_schema.TABLES 会触发元数据锁(尤其大库),且 TABLES 视图本身是动态生成的,性能开销不小。
- 生产环境建议用
PERFORMANCE_SCHEMA配合定时任务(如每小时跑一次)把结果写入自建统计表 - 避免在业务高峰查全库汇总,可先用
SHOW DATABASES缩小范围,再单库查 - 如果只是排查某张表暴涨,优先看
SHOW TABLE STATUS LIKE 'tbl_name',比走information_schema更快
别指望一条 SQL 解决所有问题——information_schema 给的是结构层估算,du 给的是文件系统真相,两者差的那部分,往往就是 TEXT 外部页、碎片、日志文件和权限隔离带来的盲区。











