查information_schema.tables时数据不准,因data_length和index_length是采样估算值,受引擎类型、统计滞后、分区表、视图及共享表空间影响;真实磁盘占用需结合ANALYZE TABLE刷新统计或du命令验证。

查 information_schema.tables 时为什么数据不准?
直接查 information_schema.tables 的 data_length 和 index_length 得到的大小,常比 du -sh 看到的实际磁盘占用小——因为 InnoDB 表空间可能包含未回收的碎片、undo log、临时段,甚至加密/压缩带来的元数据开销。更关键的是:information_schema 中的统计值只在表被打开或执行 ANALYZE TABLE 后才刷新,长时间未访问的大表很可能显示为 0 或严重滞后。
用 SUM(data_length + index_length) 按库分组再排序是否可靠?
可以快速定位大库,但无法精准排表序。原因有三:
-
data_length对于分区表,是所有分区的总和;但部分 MySQL 版本(如 5.7)对某些分区类型(如 LIST COLUMNS)统计不全 - 视图(
VIEW)在tables表中engine为NULL,data_length恒为 0,不能参与排序 - 系统库(如
mysql、performance_schema)里的表多数无真实数据页,但information_schema仍会返回非零值(尤其在启用 audit_log 插件时),干扰排序结果
所以真正要找“前十张表”,必须过滤掉视图、排除系统库、且显式指定 engine IS NOT NULL。
一条能跑通的稳定 SQL 怎么写?
以下语句在 MySQL 5.7+ 和 8.0 均可执行,已避开常见陷阱:
SELECT
table_schema AS `database`,
table_name AS `table`,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS `size_mb`
FROM information_schema.tables
WHERE
table_schema NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys')
AND engine IS NOT NULL
AND table_type = 'BASE TABLE'
ORDER BY (data_length + index_length) DESC
LIMIT 10;
注意点:
-
table_type = 'BASE TABLE'是关键,它排除了视图(VIEW)和临时表(TEMPORARY) - 用
ROUND(..., 2)避免浮点数显示过长,但别用FORMAT()—— 它返回字符串,排序会错乱 - 如果某库启用了
innodb_file_per_table=OFF,所有表共享 ibdata1,此时data_length仅反映逻辑估算值,不可信;这种情况下必须结合ls -lSh /var/lib/mysql/your_db/手动核对
想看更准的物理大小,有没有替代方案?
MySQL 自身不提供实时磁盘级表大小,但可通过 OS 层辅助验证:
- 对单个库:运行
du -sh /var/lib/mysql/your_db/*.ibd 2>/dev/null | sort -hr | head -10 - 若用通用表空间(
CREATE TABLESPACE),需额外检查对应.ibd文件路径,information_schema.innodb_sys_tablespaces可查映射关系 - Percona Toolkit 的
pt-diskusage能自动关联库表名与文件大小,适合定期巡检,但它依赖SHOW VARIABLES LIKE 'datadir'权限,且不支持 MySQL 8.0 的 data dictionary 表
真正棘手的是压缩表(ROW_FORMAT=COMPRESSED)和加密表(ENCRYPTION='Y')——information_schema 返回的仍是解压/解密前的逻辑大小,而磁盘上可能只有几分之一。这种表,光看 SQL 结果容易误判容量压力。


















