在数据库日常运维中,查询表空间占用是高频操作,但有一个常见误区:直接利用 information_schema.tables 中的 data_length 和 index_length 作为精确数据,往往会导致误判。这两个字段本质上是统计信息的估算值,当 innodb_file_per_table=OFF 时,所有表数据均存储在 ibdata1 中,查询结果要么为0,要么严重失真。要获取真实磁盘占用,必须直接查看物理文件。

MySQL表大小排查:切勿仅依赖 information_schema.tables
那么这个估算值究竟有何用处?用于快速筛选还是可行的——例如使用以下SQL扫描,排除系统库,按大小降序排列前10名,可以大致识别出哪些表可能是容量大户:
SELECT table_schema, table_name, round((data_length + index_length) / 1024 / 1024, 2) AS mb
FROM information_schema.tables
WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys')
ORDER BY mb DESC LIMIT 10;
但前提是 innodb_file_per_table=ON,且表未启用压缩或页压缩。若这些条件不满足,结果便不可信赖。在跨实例对比之前,务必先确认各实例的配置:
SHOW VARIABLES LIKE 'innodb_file_per_table';
若值为 OFF,则应跳过该SQL,转而采用文件系统层分析。此外,若表使用了 ROW_FORMAT=COMPRESSED 或 KEY_BLOCK_SIZE,data_length 反映的是压缩后的逻辑大小,实际磁盘占用可能更小(取决于文件系统块对齐),此时SQL结果会比物理大小还小,容易造成误判。
Linux环境下批量获取MySQL数据目录中表文件大小
最直接的方法是扫描 .ibd 文件。在Linux下,一条find命令即可完成,特别适用于 innodb_file_per_table=ON 的情况。需注意路径嵌套,分区表可能会生成 #P#p0 这样的子目录,但find命令能够覆盖到:
find /var/lib/mysql -name "*.ibd" -type f -printf "%s %p\n" | sort -nr | head -20 | awk '{print $1/1024/1024 " MB\t" $2}'
如果MySQL的数据目录并非默认路径,可先用以下命令确认:
mysql -e "SELECT @@datadir;"
遇到权限拒绝时,不要直接添加 sudo find——MySQL进程用户(如 mysql)可能限制了文件可见性,切换到该用户执行更为可靠:
sudo -u mysql find ...
跨服务器空间汇总时,务必注意 ibdata1 和 ib_logfile* 的干扰
当某台实例的 innodb_file_per_table=OFF 时,所有表数据都存储在 ibdata1 中,此时仅查看 .ibd 文件会完全遗漏真正的空间占用大户。而 ib_logfile* 虽属于日志文件,但常被误当作“可删除”的大文件参与容量统计,导致误判。必须单独处理。
- 检查
ibdata1大小:ls -lh /var/lib/mysql/ibdata1。如果它远大于所有.ibd的总和,说明该实例无法按表粒度定位,只能整体优化或迁移。 ib_logfile0和ib_logfile1的大小由innodb_log_file_size决定,属于固定循环写入的日志文件,不随表数据增长。跨实例容量对比时应排除它们,否则高并发实例会因日志大而“虚假上榜”。- 临时表空间
ibtmp1可能急剧膨胀(尤其在大量排序或JOIN操作时),但它在MySQL重启后会清空,不属于持久表容量,也建议过滤掉。
使用Python脚本一键拉取多实例表大小并排序
手动SSH登录每台机器效率太低,使用Python + paramiko 批量执行find命令再合并排序最为高效。关键在于将不同实例的路径、用户、过滤逻辑封装进配置,避免硬编码。
- 核心命令保持简洁:
find {datadir} -name "*.ibd" -type f -printf "%s %p\n" 2>/dev/null | head -5000(添加head防止超大实例卡死)。 - 脚本中对每行输出使用
os.path.basename()提取表名,用os.path.dirname()截取库名,再通过正则清洗掉分区后缀(如#P#p0),才能按逻辑表归并。 - 注意时区与SSH连接超时:某些旧版MySQL服务器时间不准,
paramiko默认timeout为10秒,遇到慢盘I/O容易中断,建议设置为timeout=60。
真正棘手的问题并非查询大小本身,而是查完后发现:同一张表在A实例占用50GB,在B实例却只有2GB——此时应立即检查 pt-table-checksum 或binlog位点,很有可能是主从延迟、删表未同步、或某侧开启了 innodb_stats_persistent=OFF 导致统计信息失效。这些细节若不核对,仅排列大小顺序毫无意义。
