如何从多服务器中查找占用空间最大的表_跨实例空间容量分析

作者:袖梨 2026-07-13
查MySQL表大小不能只依赖information_schema.tables,因其data_length和index_length是估算值;真实磁盘占用需结合innodb_file_per_table配置,通过扫描.ibd文件(或ibdata1)并排除日志、临时文件等干扰项来准确获取。

查 MySQL 表大小不能只看 information_schema.tables

因为 information_schema.tablesdata_lengthindex_length 是估算值,尤其在 innodb 表启用 innodb_file_per_table=off 时,所有表共用 ibdata1,这些字段会显示为 0 或严重失真。真实磁盘占用得看物理文件大小。

  • 优先用 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 且未用压缩/页压缩时结果才可信
  • 跨实例比对前,先确认各实例的 innodb_file_per_table 值:SHOW VARIABLES LIKE 'innodb_file_per_table';,值为 OFF 的实例必须跳过该 SQL,改用文件系统层分析
  • 如果表启用了 ROW_FORMAT=COMPRESSEDKEY_BLOCK_SIZEdata_length 会反映压缩后大小,但磁盘上实际占用可能更小(取决于文件系统块对齐),此时 SQL 结果反而比物理大小还小

Linux 下批量获取 MySQL 数据目录中表文件大小

直接扫 /var/lib/mysql/*/ 下的 .ibd 文件最可靠,尤其适合 innodb_file_per_table=ON 场景。注意区分表名和数据库名嵌套路径,别漏掉分区表产生的子目录。

  • 进主数据目录执行:find /var/lib/mysql -name "*.ibd" -type f -printf "%s %pn" | sort -nr | head -20 | awk '{print $1/1024/1024 " MBt" $2}'
  • 分区表的 .ibd 文件可能在 /var/lib/mysql/dbname/tablename#P#p0.ibd 这类路径下,上面命令能覆盖;但若用 mysqld --datadir 指定了非默认路径,得先用 mysql -e "SELECT @@datadir;" 确认真实路径
  • 遇到权限拒绝,别直接加 sudo find——MySQL 进程用户(如 mysql)可能限制了文件可见性,应切换到该用户执行:sudo -u mysql find ...

跨服务器汇总时别忽略 ibdata1ib_logfile* 的干扰

当某台 MySQL 实例 innodb_file_per_table=OFF,所有表数据都挤在 ibdata1 里,这时单看 .ibd 文件会完全漏掉真实主力占用。而 ib_logfile* 虽然属于日志,但常被误当成“可删”大文件参与容量统计,导致误判。

  • 检查 ibdata1 大小:ls -lh /var/lib/mysql/ibdata1;若远大于所有 .ibd 总和,说明该实例必须单独处理:无法按表粒度定位,只能整体优化或迁移
  • ib_logfile0ib_logfile1 大小由 innodb_log_file_size 决定,是固定循环写入的日志,不随表增长——跨实例比容量时应排除它们,否则高并发实例会因日志大而“虚假上榜”
  • 临时表空间 ibtmp1 可能暴涨(尤其大量排序/JOIN),但它会在 MySQL 重启后清空,不属于持久表容量,也建议过滤

Python 脚本一键拉取多实例表大小并排序

手动 ssh 登每台机器太慢,用 Python + paramiko 批量执行 find 命令再合并排序最省事。关键是把不同实例的路径、用户、过滤逻辑封装进配置,避免硬编码。

  • 核心命令保持简洁:find {datadir} -name "*.ibd" -type f -printf "%s %pn" 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 导致统计信息失效。这些细节不核对,光排大小顺序没意义。

相关文章

精彩推荐