
如果有人问你你的数据库到底有多大先别急着回答。因为这个问题至少有 5 种解读方式磁盘上数据文件占了多大逻辑上一共有多少行数据索引膨胀成了什么规模日志文件又占了多少空间备份文件要额外预留多少Hacker News 上的热门帖子 Ask HN: What is your database size讨论的就是这个看似简单、其实特别容易踩坑的问题。社区里大家晒出来的数字各不相同有几 GB 的小库也有几 TB 的业务库。真正有价值的不是某个数字本身而是“你用的哪种衡量口径”。数据库大小直接影响到备份恢复时间、迁移成本、查询性能、磁盘告警阈值也常常是各种连接失败、数据库恢复异常、升级卡住的根因。这篇文章我会从口径、查询方法、容量规划、膨胀原因、瘦身治理、自动化巡检和排查思路几个维度展开帮你把“数据库大小”这件事彻底搞清楚。文章适合数据库管理员、后端开发、运维同学也适合刚接手一个存量项目、第一件事就想摸清数据库家底的工程师。最后我会给出一套可以直接复制执行的 SQL 和 Python 脚本你可以绕开手工点界面直接批量盘点所有库和表的体积。1. 数据库大小的几种口径与核心关注指标“数据库大小”不是一个单独的数字至少要区分下面几类指标含义典型查看方式主要影响逻辑数据量表中实际有多少行、多少条记录SELECT count(*)影响查询执行计划、索引选择数据文件大小表空间或文件组里数据文件占用的磁盘空间系统目录视图、information_schema影响磁盘占用、备份体积索引大小所有二级索引占用的空间pg_indexes_size、SHOW INDEX影响写入性能、备份体积日志大小WAL、redo、undo、binlog、事务日志的累积大小数据库日志目录、DBCC影响可用磁盘空间、恢复时间备份大小逻辑备份或物理备份压缩后的体积备份文件大小统计影响备份存储成本和恢复时间内存形态大小热数据在缓冲池/缓存中的占用数据库状态指标影响缓存命中率和响应速度从运维角度最需要盯的是“数据文件大小 日志大小 备份大小”。很多故障并不是业务数据真的把磁盘塞满而是事务日志没有截断、索引碎片严重、老数据没有归档导致物理占用持续膨胀。2. 为什么需要关注数据库大小空间、性能与运维数据库体积并不是“放得下就行”。从实际运维经验看至少在这几个方面必须把大小当作前置条件来管理。首先是磁盘空间。数据库是持续增长的系统一旦数据文件把所在分区占满轻则写入失败重则数据库实例直接进入只读或异常恢复状态。比如常见的could not create connection to database server有时候并不是连接数不够而是磁盘满了导致后台进程无法写入临时文件。其次是备份和恢复窗口。数据库越大全量备份时间越长恢复时间也就越长。一个 100GB 的数据库和 2TB 的数据库容灾策略完全不同。前者可能每天全备就够了后者往往需要结合增量备份、物理复制或延迟备库来做。第三是查询性能。数据库物理体积大并不代表所有查询都慢但以下几种情况会明显变差表数据量级从千万涨到亿级后某些未走索引的查询会从秒级变成分钟级。索引碎片化严重扫描的物理页数增多内存命中率下降。事务日志膨胀长时间没有 checkpoint恢复时花费的时间也会变长。最后是迁移和升级。在做数据库迁移、大版本升级、跨机房同步时数据库大小直接决定了迁移方案。大库通常不能用简单的dumpimport得考虑数据同步工具、停机窗口、增量追平。热词里出现的 OracleORA-14694: database must in upgrade mode to begin max_string_size migration本质上就是升级过程中数据库处于“不允许直接操作”的状态这种操作更需要提前盘点库体积避免在磁盘空间和恢复时间上失控。所以关注数据库大小不是单纯为了“看数字”而是为了做容量管理、性能调优和故障前置处理。3. 五种主流数据库查看数据库大小的具体方法下面给出常见数据库系统的查询方法。这些 SQL 不需要额外工具直接在客户端执行即可但不同版本可能有细微差异实际使用时以目标环境版本为准。3.1 PostgreSQL查看单个数据库大小SELECT pg_size_pretty(pg_database_size(your_database_name));查看所有数据库大小SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size FROM pg_database ORDER BY pg_database_size(datname) DESC;查看当前数据库中所有表的大小含索引SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname || . || tablename)) AS total_size FROM pg_tables WHERE schemaname NOT IN (pg_catalog, information_schema) ORDER BY pg_total_relation_size(schemaname || . || tablename) DESC;如果只是想看表数据本身不带索引可以把pg_total_relation_size换成pg_relation_size。如果发现某个表total_size很大但逻辑行数不多基本可以判断是膨胀或索引冗余。3.2 MySQL / MariaDB通过information_schema统计各库的物理大小SELECT table_schema AS database, ROUND(SUM(data_length index_length) / 1024 / 1024, 2) AS size_mb FROM information_schema.tables GROUP BY table_schema ORDER BY SUM(data_length index_length) DESC;查看每个表的大小SELECT table_schema AS database, table_name, ROUND((data_length index_length) / 1024 / 1024, 2) AS size_mb, table_rows FROM information_schema.tables WHERE table_schema NOT IN (information_schema, mysql, performance_schema, sys) ORDER BY (data_length index_length) DESC;注意table_rows是估算值不精确尤其对于 InnoDB。真实行数仍需要COUNT(*)验证。3.3 SQL Server查看当前实例所有数据库的数据和日志文件大小SELECT DB_NAME(database_id) AS database_name, TYPE_DESC AS file_type, name AS file_name, CAST(size AS BIGINT) * 8 / 1024 AS size_mb, CAST(FILEPROPERTY(name, SpaceUsed) AS BIGINT) * 8 / 1024 AS used_mb, CAST(FILEPROPERTY(name, SpaceUsed) AS BIGINT) * 100.0 / NULLIF(size, 0) AS used_percent FROM sys.master_files;也可以使用sp_spaceused查看当前库的总体信息USE your_database_name; EXEC sp_spaceused;3.4 OracleOracle 查看整个表空间总使用量SELECT tablespace_name, ROUND(SUM(bytes) / 1024 / 1024 / 1024, 2) AS size_gb FROM dba_segments GROUP BY tablespace_name ORDER BY size_gb DESC;查看当前用户下所有段对象的大小SELECT segment_name, segment_type, ROUND(bytes / 1024 / 1024, 2) AS size_mb FROM user_segments WHERE segment_type IN (TABLE, INDEX) ORDER BY bytes DESC;对于 Oracle 12c还需要注意PDB和CDB的区别。查询容器数据库时需要进到具体的 PDB 里执行否则统计的是整个 CDB 的全局数据。3.5 MongoDBMongoDB 在mongosh里执行db.stats();查看某个集合大小db.collection.stats();注意dataSize表示文档数据量storageSize表示磁盘实际占用totalSize包含索引。两者差距大往往说明有删除操作后空间没有立即释放需要compact或规划整理。3.6 RedisRedis 是内存型数据库大小主要体现在内存占用redis-cli INFO memory关注used_memory_human和used_memory_peak_human。如果used_memory接近maxmemory说明容量快满了需要淘汰策略或扩容。4. 数据库膨胀的典型原因与故障现象数据库大小不是线性稳定的经常会出现“数据没涨多少磁盘占用却翻倍”的情况。从实际排查经验看常见原因有四类第一类是无主键或大字段表过度膨胀。频繁的INSERT、UPDATE、DELETE会在表文件和索引文件里产生大量空洞。PostgreSQL 的 MVCC 机制会保留旧版本行如果没有及时VACUUM表体积会持续增长MySQL InnoDB 的 purge 线程跟不上删除速度也会出现类似问题。第二类是事务日志没有正常截断。SQL Server 的FULL恢复模式如果长期不做日志备份ldf文件会一路涨到磁盘爆满。wait on the database engine recovery handle failed这类错误经常出现在日志大量积压、数据库恢复异常的场景里。MySQL 的 binlog 和 relay log 如果没有合理过期时间也会占掉大量磁盘。第三类是索引设计冗余。同一个表上建了三四个相似联合索引且永远没有被使用每次写入都要维护多份索引占用空间成倍增加。第四类是历史数据只增不减。业务流水、日志表、操作记录表只做插入不做归档一年之后物理体积轻松翻几倍。下面是数据库膨胀后常见的故障现象现象可能的数据库大小原因连接失败could not create connection to database server磁盘满服务不可写或后台进程异常备份时间越来越长数据库总体积增长备份策略未调整查询明明加了索引还是慢索引碎片化严重或表膨胀导致扫描块增多数据库启动卡在恢复阶段日志文件过大崩溃恢复需要扫描大量 LSN热词中的 ORA、Master Database 访问异常空间不足、升级操作前未做充分容量检查遇到这类报错不要只盯着错误代码先检查磁盘使用率、数据文件占用、日志目录大小往往能快速定位根因。5. 数据库容量规划预留多少空间才算合理容量规划的目标是在业务增长和成本之间找平衡。如果只预留 20% 余量一个大促、一次批量导入就可能打满磁盘如果预留 200%资源闲置成本又太高。更稳的做法是把数据库占用拆成几个部分估算组成部分估算方式建议业务数据文件当前数据大小 日均增长量 × 保留周期按季度滚动回顾索引文件通常为数据文件的 20% 到 60%取决于索引数量定期清理冗余索引事务日志/WAL/binlog高峰时段日志增长速度 × 最长故障恢复时间单独监控单独规划临时表空间/tempdb最大排序、哈希操作所需空间建议与数据文件分盘备份文件全备体积 × 保留份数 增量备份周期体积备份存储独立计算系统预留余量以上总和 × 15% 到 25%避免紧急扩容举例说明某个 MySQL 库当前数据文件 200GB索引 60GB预计半年增长 30%全备压缩后 100GB保留 7 份全备那么磁盘至少需要满足数据文件 索引200 60 260GB 半年增长后260 * 1.3 338GB 业务侧建议余量338 * 1.2 405GB 备份存储100 * 7 700GB单独磁盘如果日志文件单独分区还需要加上日志峰值。从这个角度看“数据库多大”并不是单一磁盘容量问题而是一个由业务数据、索引、日志、备份共同决定的总拥有成本。6. 数据库瘦身与治理实战当数据库体积已经偏大最有效的动作不是盲目扩容而是做瘦身治理。下面按“风险从低到高”的顺序介绍。6.1 清理历史归档数据把超过保留周期的数据迁移到归档表、冷存储或数据仓库再删除原表数据。清理前务必确认有完整备份且已经验证过恢复。业务侧做了灰度验证。删除操作放在低峰期执行。大批量删除时分批提交避免锁表和事务日志暴增。PostgreSQL 大批量清理示例-- 分批删除每批 10000 行避免长事务 DELETE FROM operations WHERE created_at 2024-01-01 LIMIT 10000;如果表需要频繁删除历史数据建议直接使用分区表每月或每季度一个分区过期的分区DROP TABLE比DELETE快得多。6.2 清理索引碎片碎片化的索引既占空间又拖慢查询。在 PostgreSQL 中重建索引REINDEX INDEX index_name;MySQL 中整理表并回收空间OPTIMIZE TABLE your_table_name;注意OPTIMIZE TABLE会锁表最好在业务低峰期执行。SQL Server 中可以使用ALTER INDEX ... REBUILDALTER INDEX IX_your_index ON dbo.your_table REBUILD;重建索引之前先评估有没有未被使用的索引有的话优先删除。可以通过 MySQL 的performance_schema或 PostgreSQL 的pg_stat_user_indexes查看索引使用情况长期idx_scan为 0 的索引可以考虑下线。6.3 压缩表和行格式MySQL InnoDB 可以考虑调整行格式或开启表压缩但压缩会增加 CPU 开销需要结合业务读写比例测试后决定。Oracle 可以使用ALTER TABLE your_table MOVE TABLESPACE your_tablespace;执行后段空间会被重新整理。MOVE操作会锁表且需要额外的空闲空间。6.4 收缩日志文件SQL Server 日志文件膨胀时先检查恢复模式SELECT name, recovery_model_desc FROM sys.databases;如果是FULL模式应该先做日志备份再收缩日志文件BACKUP LOG your_database TO DISK NUL; DBCC SHRINKFILE (your_database_log, 1024);MySQL 则通过设置合理的 binlog 过期时间SET GLOBAL binlog_expire_logs_seconds 604800;6.5 PostgreSQL 表膨胀治理PostgreSQL 在大量更新删除后表文件可能残留大量死元组。处理顺序建议是VACUUM (VERBOSE, ANALYZE) your_table;如果pg_total_relation_size仍然很大再考虑VACUUM FULL your_table;VACUUM FULL会获取ACCESS EXCLUSIVE锁在线业务需要谨慎尽量在维护窗口执行。7. 自动化巡检与批量统计数据库大小的日常管理不能每次都手输SELECT。建议做一套自动化巡检把“盘点库和表大小”变成每天自动执行的脚本。7.1 PostgreSQL 批量统计脚本使用psql循环查询所有数据库大小#!/bin/bash # 按需替换连接参数 export PGPASSWORDyour_password for db in $(psql -h 127.0.0.1 -U postgres -d postgres -t -c \ SELECT datname FROM pg_database WHERE datistemplate false;); do psql -h 127.0.0.1 -U postgres -d $db -c \ SELECT current_database(), pg_size_pretty(pg_database_size(current_database())); done7.2 Python 巡检 MySQL 所有表用pymysql连接实例一次性输出所有表的大小清单import pymysql conn pymysql.connect( host127.0.0.1, port3306, usermonitor_user, passwordyour_password, databaseinformation_schema ) cursor conn.cursor() cursor.execute( SELECT table_schema, table_name, ROUND((data_length index_length) / 1024 / 1024, 2) AS size_mb, table_rows FROM tables WHERE table_schema NOT IN (information_schema, mysql, performance_schema, sys) ORDER BY (data_length index_length) DESC LIMIT 50; ) for row in cursor.fetchall(): print(f库: {row[0]}, 表: {row[1]}, 大小: {row[2]} MB, 估算行数: {row[3]}) cursor.close() conn.close()该脚本可以直接接到定时任务里输出到 CSV 或日志文件再配合告警规则使用。7.3 定时任务建议# crontab 示例每天凌晨 2 点执行巡检脚本 0 2 * * * /opt/scripts/db_size_check.py /var/log/db_size_check.log 21需要注意巡检账号不应该用root或高权限账号建议单独创建只读账号并只授予查询系统视图的权限。比如 MySQLCREATE USER monitor_userlocalhost IDENTIFIED BY your_password; GRANT SELECT ON information_schema.* TO monitor_userlocalhost;8. 资源占用与性能观察方法数据库大小变化会直接体现在系统资源指标上。日常观察时建议同时关注以下几项。指标观察方法与数据库大小的关系磁盘使用率df -h、监控大盘数据文件、日志、备份是否接近上限IO 延迟iostat、数据库慢查询日志表扫描量增大逻辑读和物理读增加缓冲池命中率MySQLSHOW GLOBAL STATUS LIKE Innodb_buffer_pool_read%热数据能否被缓存数据库越大越明显主从延迟SHOW REPLICA STATUS或pg_stat_replication大事务、大批量操作会拖慢从库慢查询数量慢查询日志、审计平台表膨胀后原本正常的 SQL 可能变慢如果要降低数据库膨胀对性能的影响优先做三件事第一保证缓冲池/共享内存能覆盖大部分热数据第二对大表做分区把查询裁剪到更小的分区第三定期整理碎片减少不必要的 IO。9. 常见问题与排查方法问题现象可能原因排查方式解决方案磁盘使用率达到 100%数据库无法写入数据文件、日志或临时文件打满df -h查看大文件目录清理日志、归档数据扩容磁盘数据库启动卡在恢复阶段日志文件过大或上次异常关闭需要回放大量日志检查错误日志和数据库恢复状态等待恢复完成评估日志频繁备份执行OPTIMIZE TABLE或VACUUM FULL后空间没降高水位线未回落或者空间被其他文件占用对比表文件大小和数据实际占用重建表、使用分区表、分盘存储备份文件比业务库还大很多备份未压缩或历史备份保留太多查看备份策略文件列表启用压缩、调整保留周期一个表显示几 GB但count(*)只有几十万行表碎片化严重、存在大字段或索引冗余查看表物理大小和各索引大小重建索引、清理冗余索引、迁移大字段数据库连接报错应用一直重连连接数打满或磁盘满导致实例异常查看连接数和磁盘使用率释放连接、扩容磁盘、重启实例如果遇到类似热词中 Oracle 升级、SQL Server 恢复失败这类错误不要直接套用记忆里的命令先在测试库复现再在生产环境低峰期操作。必须确认当前数据库状态、空间余量和备份可用性。10. 最佳实践与合规建议最后给一组可以直接落地的建议按优先级排列。先建一个“数据库大小基线表”。每周记录每个库的数据文件、日志、备份大小连续观察 4 周就能看出增长趋势。大表优先使用分区。按月分区删除和归档成本会大幅下降。把备份存储和数据存储分开。不要让备份文件占用业务盘的容量空间。日志备份要纳入例行任务。FULL 恢复模式的数据库必须定期备份事务日志避免日志无限增长。巡检账号最小权限。只读账号就只授SELECT避免在巡检脚本里泄露高权限密码。涉及用户数据、隐私数据时归档和数据清理要遵守合规要求。不要因为“数据库太大”就随意物理删除确认数据保留期限和授权范围之后再处理。做任何收缩、清理、重建操作前先把备份验证一遍。没有可恢复备份的 DDL 操作都是高风险操作。回到最初的问题你的数据库到底有多大从今天开始先别回答数字。先跑一遍查询把数据文件、日志、备份三列数字列出来再盯一周增长趋势你才真正掌握了这个数据库的“体量”。这是数据库运维的基础也是很多诡异故障排查的起点。