十五年数据库相关经验,做过 DBA、架构师、技术顾问。不求"颠覆",只求"靠谱"。
数据库磁盘满了才发现,是 DBA 最尴尬的时刻。
这种事听起来像笑话,但真发生过不止一次。业务正常运行,数据库没有告警,有一天早上突然所有写入失败——磁盘 100% 满了。紧急删临时文件、紧急扩容、紧急重启。业务中断几小时,老板问"为什么没有预警",你答不上来。
磁盘满了是最基本的容量事故。比这更隐蔽的是:连接数打满、内存不够用、CPU 长期 90%+——这些容量瓶颈不是一天出现的,是数据量增长、业务量增长慢慢积累的。积累到临界点之前,一切正常。到了临界点,突然就不行了。
今天把数据库容量规划的完整方法讲清楚。从数据增长预测、瓶颈识别、扩容方案到自动化预警。跟着这个流程走一遍,下次磁盘不会在凌晨三点满。
01 数据增长预测:你的数据库还能撑多久
容量规划的第一步是知道"现在有多少"和"每天涨多少"。
当前容量:不只是磁盘
很多人以为容量就是磁盘用了多少。不是。数据库的容量瓶颈有四个维度:
| 维度 | 怎么查 | 临界点 |
|---|---|---|
| 磁盘空间 | df -h(Linux),数据库数据目录大小 | 80% 告警,90% 危险 |
| 连接数 | SHOW PROCESSLIST / pg_stat_activity | 最大连接数的 70% 告警 |
| 内存 | 数据库缓存命中率、swap 使用情况 | swap 开始出现就是危险信号 |
| CPU | top,数据库 CPU 利用率 | 持续 80%+ 需要关注 |
磁盘是最直观的,但连接数和内存才是更容易突然爆炸的。
增长率:看趋势,不看单点
知道当前用了多少不够,关键要知道每天涨多少、每月涨多少。
简单的方法:每天固定时间记录数据库大小,连续记录 30 天,算日均增长率。
-- MySQL:查看数据库大小
SELECT table_schema AS "Database",
ROUND(SUM(data_length + index_length) / 1024 / 1024 / 1024, 2) AS "Size_GB"
FROM information_schema.tables
GROUP BY table_schema;
-- PostgreSQL:查看数据库大小
SELECT pg_database.datname,
pg_size_pretty(pg_database_size(pg_database.datname)) AS size
FROM pg_database;
连续记录后,你得到的是一个趋势,不是一个点。趋势才有预测价值。
预测:还能撑多久
有了日均增长率,算剩余天数很简单:
剩余天数 = (磁盘总容量 × 80% - 当前已用) / 日均增长量
注意用的是 80%,不是 100%。因为到了 80% 就该告警了,不能等到 100%。
实测案例:一个生产库当前 500GB,日均增长 2GB,磁盘 1TB。按 80% 算,剩余可用空间 = 800GB - 500GB = 300GB,还能撑 150 天。但 150 天后正好是业务旺季,日均增长可能变成 5GB。那实际只能撑 60 天。
所以预测不能只看线性趋势,还要考虑业务季节性。大促前、月底出账前、节假日前——这些时段的增长率要单独算。
02 容量瓶颈识别:不是所有瓶颈都在磁盘上
磁盘满了是最容易发现的瓶颈。但有些瓶颈更隐蔽。
连接数瓶颈
表现:应用偶尔报"too many connections",但不是每次都报。重启数据库后恢复正常,过几天又出现。
根因:连接池配置不合理,或者应用有连接泄漏。最大连接数设了 500,高峰期用了 480,再多几个连接就爆了。
怎么查:
-- MySQL:当前连接数
SHOW STATUS LIKE 'Threads_connected';
-- 历史最大连接数
SHOW STATUS LIKE 'Max_used_connections';
如果 Max_used_connections 接近 max_connections,说明连接数不够了。
内存瓶颈
表现:数据库偶尔变慢,没有明显的慢 SQL,但整体响应时间变长。有时候甚至开始用 swap。
根因:数据库缓存(buffer pool / shared buffers)不够大,热点数据无法全部缓存,频繁从磁盘读。或者某个查询的排序/哈希操作太大,内存不够用,溢出到磁盘。
怎么查:
-- MySQL:Buffer Pool 命中率
SELECT (1 - Variable_value / (
SELECT Variable_value FROM information_schema.global_status
WHERE Variable_name = 'Innodb_reads'
)) * 100 AS buffer_pool_hit_rate
FROM information_schema.global_status
WHERE Variable_name = 'Innodb_buffer_pool_reads';
-- PostgreSQL:缓存命中率
SELECT sum(heap_blks_read) AS disk_reads,
sum(heap_blks_hit) AS cache_hits,
round(sum(heap_blks_hit) * 100.0 / (sum(heap_blks_read) + sum(heap_blks_hit)), 2) AS hit_rate
FROM pg_statio_user_tables;
命中率低于 95% 说明缓存不够。但要先确认不是统计信息过时导致的异常值。
CPU 瓶颈
表现:高峰期数据库响应时间变长,但磁盘 I/O 和内存都没问题。
根因:通常是大量复杂查询同时执行,或者某个查询的执行计划突然变了(比如统计信息过时),导致全表扫描。
怎么查:用 top 看数据库进程的 CPU 占比,再用数据库的慢查询日志定位具体 SQL。
踩坑提醒:不要只看"当前"的资源利用率,要看"趋势"。CPU 平时 30%,这周开始 60%,下周可能就到 90% 了。趋势比当前值更有预警价值。
03 扩容方案对比:怎么选
发现容量不够了,怎么办?三种路径。
垂直扩容:加硬件
加内存、加 CPU、换更大的磁盘。最简单直接。
优点:不用改架构,不用改代码,立竿见影。 缺点:有上限。单机性能不可能无限增长,到了硬件极限就只能走别的路径。而且贵。
适用场景:容量瓶颈还不严重,数据量在单机可承受范围内。这是第一步该试的方案。
水平分片:分库分表
把数据分到多个数据库实例上。每个实例只存一部分数据。
优点:理论上没有上限,可以无限横向扩展。 缺点:架构复杂度飙升。跨库 JOIN、分布式事务、数据迁移——每一个都是大工程。
适用场景:垂直扩容已经到头了,单表数据量超过 5 亿行,单机性能到达物理极限。
归档清理:把不用的数据挪走
把历史数据归档到冷存储,主库只保留热数据。
优点:不增加硬件,不改变架构。主库数据量减小,性能自然回升。 缺点:归档数据的查询变慢(冷存储不是实时查询的),归档策略需要业务方确认。
适用场景:数据库里存了大量历史数据,但业务上很少查。比如日志表、操作记录表、过期订单表。
对比总结
| 方案 | 成本 | 复杂度 | 效果 | 适用阶段 |
|---|---|---|---|---|
| 垂直扩容 | 中(硬件成本) | 低 | 立即见效,但有上限 | 早期瓶颈 |
| 水平分片 | 高(架构改造) | 极高 | 理论上无上限 | 晚期瓶颈 |
| 归档清理 | 低(存储成本) | 中 | 视归档量而定 | 中期,有大量历史数据 |
04 自动化预警:别等满了才报警
容量规划不是一次性工作,是持续监控。靠人工每天查数据库大小是不现实的,必须自动化。
监控什么
| 指标 | 告警阈值 | 频率 |
|---|---|---|
| 磁盘使用率 | > 80% 预警,> 90% 紧急 | 每 5 分钟 |
| 数据增长率 | 超过基线 50% | 每天 |
| 连接数使用率 | > 70% 最大连接数 | 每 5 分钟 |
| 缓存命中率 | < 95% | 每 15 分钟 |
| CPU 利用率 | 持续 > 80% 超过 10 分钟 | 每 5 分钟 |
| 剩余可支撑天数 | < 30 天 | 每天 |
怎么告警
- 磁盘/连接数:超过阈值直接告警(短信/钉钉/企微)
- 增长率异常:对比历史基线,突增 50%+ 时告警
- 剩余天数:30 天内磁盘满 → 告警;60 天内磁盘满 → 预警
踩坑提醒:告警不能只报"磁盘 85% 了",要报"磁盘 85%,按当前增长率 15 天后满,建议本周内扩容"。带预测的告警才有行动价值。
对比:被动扩容 vs 主动容量规划
| 维度 | 被动扩容(磁盘满了再扩) | 主动容量规划 |
|---|---|---|
| 业务影响 | 停机扩容,业务中断 | 提前扩容,业务无感知 |
| 成本 | 紧急采购,价格高 | 计划采购,价格低 |
| 风险 | 数据丢失风险 | 可控 |
| DBA 压力 | 半夜被叫起来救火 | 按计划执行 |
| 扩容方式 | 临时方案,可能不是最优 | 有充分时间评估方案 |
决策框架:按数据量选择容量策略
| 数据量 | 推荐策略 | 说明 |
|---|---|---|
| < 100GB | 垂直扩容 + 定期清理 | 单机轻松扛,不需要复杂方案 |
| 100GB - 1TB | 垂直扩容 + 归档清理 + 读写分离 | 单机接近上限,开始考虑分流 |
| 1TB - 5TB | 读写分离 + 归档 + 评估分片 | 单机压力大,需要多策略组合 |
| > 5TB | 分库分表 + 冷热分离 | 单机无法承载,必须水平扩展 |
态度转变:我以前也不做容量规划
干 DBA 前三年,我管理数据库的方式是"满了再加"。
磁盘满了?加一块盘。连接数不够了?调大 max_connections。内存不够了?加内存条。简单粗暴,但确实能解决问题。
直到有一次,数据库在凌晨两点磁盘满了。所有写入失败,包括业务写入和日志写入。更糟的是,数据库因为写不了 WAL 文件,自动 crash 了。重启时发现磁盘满了,启动失败。手动删了几个大日志文件才启动起来。恢复数据花了两个小时,业务中断两小时。
那次之后,我做了几件事:第一,所有数据库的磁盘使用率监控加到告警系统里。第二,每天自动记录数据库大小,算增长率,预测剩余天数。第三,每月做一次容量评估报告,发给相关负责人。
从那以后,再也没在凌晨三点被磁盘满的告警叫起来过。
深度分析:为什么容量规划容易被忽视
容量规划不像性能优化那样"立竿见影"。优化一个 SQL,延迟从 5 秒降到 50ms,老板立刻能看到效果。容量规划的效果是"什么都没发生"——磁盘没满、连接数没爆、数据库没挂。
但"什么都没发生"恰恰是容量规划最大的价值。你不需要向老板解释"为什么今天数据库没挂",你只需要确保它不挂。
另一个原因是容量问题的"温水煮青蛙"特性。磁盘不是一天满的,是每天涨 1%、2%,涨到某一天突然满了。在 80% 之前,没人觉得有问题。到了 90%,开始紧张。到了 95%,紧急扩容。但如果在 50% 的时候就开始关注,有充足的时间做规划。
容量规划月度巡检检查清单
磁盘容量
- 当前磁盘使用率和剩余空间
- 近 30 天数据增长趋势
- 按增长率预测剩余可支撑天数
- 大表 Top 10 及增长趋势
- 临时文件和日志文件大小
连接数
- 当前连接数和历史最大值
- 最大连接数配置是否合理
- 是否有连接泄漏(长时间空闲的连接)
内存
- 缓存命中率趋势
- swap 使用情况(不应使用)
- 数据库内存参数是否需要调整
CPU
- CPU 利用率趋势(日均、峰值)
- Top 10 CPU 消耗 SQL
- 是否有执行计划突变的 SQL
预警和规划
- 30 天内是否需要扩容
- 60 天内是否需要扩容
- 扩容方案已评估(垂直/水平/归档)
- 容量评估报告已发送给相关负责人
总结
数据库容量规划的核心不是"查磁盘用了多少",是"预测还能撑多久"。
数据增长趋势比当前用量重要,剩余天数比剩余 GB 数重要,主动预警比被动救火重要。容量瓶颈也不只是磁盘——连接数、内存、CPU 都需要持续监控。
靠人工查是不可持续的,必须自动化监控 + 自动预警。预警不能只报"磁盘 85%",要报"按当前增长率 15 天后满"。带预测的预警才有行动价值。
不要等磁盘满了才想起来做容量规划。每个月花一个小时跑一遍巡检清单,比凌晨三点被叫起来救火强得多。
后续我会继续分享数据库安全审计、权限管理这些话题,跟着我一篇篇学,数据库这块就没问题了。
有问题评论区见。
十五年数据库领域老炮。关注我,一起把数据库这件事搞明白。