数据库容量瓶颈不只是磁盘——连接数和内存才是隐形杀手

4 阅读11分钟

十五年数据库相关经验,做过 DBA、架构师、技术顾问。不求"颠覆",只求"靠谱"。


数据库磁盘满了才发现,是 DBA 最尴尬的时刻。

这种事听起来像笑话,但真发生过不止一次。业务正常运行,数据库没有告警,有一天早上突然所有写入失败——磁盘 100% 满了。紧急删临时文件、紧急扩容、紧急重启。业务中断几小时,老板问"为什么没有预警",你答不上来。

磁盘满了是最基本的容量事故。比这更隐蔽的是:连接数打满、内存不够用、CPU 长期 90%+——这些容量瓶颈不是一天出现的,是数据量增长、业务量增长慢慢积累的。积累到临界点之前,一切正常。到了临界点,突然就不行了。

今天把数据库容量规划的完整方法讲清楚。从数据增长预测、瓶颈识别、扩容方案到自动化预警。跟着这个流程走一遍,下次磁盘不会在凌晨三点满。


01 数据增长预测:你的数据库还能撑多久

容量规划的第一步是知道"现在有多少"和"每天涨多少"。

当前容量:不只是磁盘

很多人以为容量就是磁盘用了多少。不是。数据库的容量瓶颈有四个维度:

维度怎么查临界点
磁盘空间df -h(Linux),数据库数据目录大小80% 告警,90% 危险
连接数SHOW PROCESSLIST / pg_stat_activity最大连接数的 70% 告警
内存数据库缓存命中率、swap 使用情况swap 开始出现就是危险信号
CPUtop,数据库 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 天后满"。带预测的预警才有行动价值。

不要等磁盘满了才想起来做容量规划。每个月花一个小时跑一遍巡检清单,比凌晨三点被叫起来救火强得多。

后续我会继续分享数据库安全审计、权限管理这些话题,跟着我一篇篇学,数据库这块就没问题了。

有问题评论区见。


十五年数据库领域老炮。关注我,一起把数据库这件事搞明白。