AI 容量规划:基于负载预测的存储资源自动扩缩容策略
一、容量规划不是"拍脑袋加机器",是数据驱动的资源博弈
每到季度末,存储团队都会面对一个灵魂拷问:"下个季度需要加多少机器?"拍脑袋的回答要么导致资源浪费(加多了),要么导致线上事故(加少了)。更棘手的是,存储资源的需求不是线性增长的——业务活动(大促、新功能上线)会导致突发流量,数据生命周期(冷热分离、过期清理)会导致容量波动。
传统容量规划依赖经验公式:当前用量 × 增长率 × 安全系数。但增长率本身在变化,安全系数是拍脑袋的。AI 辅助容量规划的核心价值是:基于历史负载数据训练预测模型,输出带置信区间的容量预测,并自动生成扩缩容方案。不是替代人的判断,而是把"拍脑袋"变成"看数据"。
二、AI 容量规划的架构与预测链路
flowchart TB
A[历史负载数据] –> B[特征工程]
B –> B1[时间特征<br/>小时/星期/月份]
B –> B2[周期特征<br/>业务周期/活动周期]
B –> B3[趋势特征<br/>7天/30天移动平均]
B1 –> C[预测模型]
B2 –> C
B3 –> C
C –> C1[Prophet<br/>趋势+季节+节假日]
C –> C2[LSTM<br/>长短期记忆网络]
C –> C3[集成模型<br/>加权平均]
C1 –> D[容量预测]
C2 –> D
C3 –> D
D –> D1[预测值<br/>未来7-30天]
D –> D2[置信区间<br/>80%/95%]
D –> D3[异常概率<br/>超阈值风险]
D1 –> E[扩缩容决策]
D2 –> E
D3 –> E
E –> E1[扩容方案<br/>何时加/加多少]
E –> E2[缩容方案<br/>何时减/减多少]
E –> E3[成本估算<br/>资源费用预测]
style C fill:#e3f2fd
style D2 fill:#fff3e0
style E fill:#e8f5e9
AI 容量规划的三层架构:特征工程层(从原始负载数据中提取时间、周期和趋势特征)、预测模型层(Prophet 处理趋势和季节性,LSTM 捕捉非线性模式,集成模型综合两者)、决策层(基于预测值和置信区间生成扩缩容方案)。置信区间是关键——它告诉决策者"预测可能错多少",而不是只给一个点估计。
三、代码实现与分析
3.1 负载特征工程
from __future__ import annotations
import numpy as np
import pandas as pd
from dataclasses import dataclass
from datetime import datetime, timedelta
@dataclass
class CapacityMetrics:
"""容量指标"""
timestamp: datetime
cpu_utilization: float # CPU 利用率
memory_usage_gb: float # 内存使用量
disk_usage_gb: float # 磁盘使用量
disk_total_gb: float # 磁盘总量
iops_read: int # 读 IOPS
iops_write: int # 写 IOPS
qps: int # 查询 QPS
replication_lag_ms: float # 复制延迟
@property
def disk_usage_ratio(self) -> float:
return self.disk_usage_gb / max(self.disk_total_gb, 1)
class FeatureEngineer:
"""负载特征工程"""
def extract_features(
self, metrics: list[CapacityMetrics]
) -> pd.DataFrame:
"""从原始指标中提取预测特征"""
df = pd.DataFrame([
{
"timestamp": m.timestamp,
"cpu_utilization": m.cpu_utilization,
"memory_usage_gb": m.memory_usage_gb,
"disk_usage_gb": m.disk_usage_gb,
"disk_usage_ratio": m.disk_usage_ratio,
"iops_read": m.iops_read,
"iops_write": m.iops_write,
"qps": m.qps,
}
for m in metrics
])
df["timestamp"] = pd.to_datetime(df["timestamp"])
df = df.set_index("timestamp").sort_index()
# 时间特征
df["hour"] = df.index.hour
df["day_of_week"] = df.index.dayofweek
df["day_of_month"] = df.index.day
df["month"] = df.index.month
df["is_weekend"] = (df.index.dayofweek >= 5).astype(int)
# 周期特征:正弦/余弦编码
df["hour_sin"] = np.sin(2 * np.pi * df["hour"] / 24)
df["hour_cos"] = np.cos(2 * np.pi * df["hour"] / 24)
df["dow_sin"] = np.sin(2 * np.pi * df["day_of_week"] / 7)
df["dow_cos"] = np.cos(2 * np.pi * df["day_of_week"] / 7)
# 趋势特征
df["disk_usage_7d_ma"] = df["disk_usage_gb"].rolling(7 * 24, min_periods=1).mean()
df["disk_usage_30d_ma"] = df["disk_usage_gb"].rolling(30 * 24, min_periods=1).mean()
df["disk_growth_rate"] = (
df["disk_usage_7d_ma"] – df["disk_usage_30d_ma"]
) / df["disk_usage_30d_ma"].replace(0, np.nan)
# 波动特征
df["disk_usage_7d_std"] = df["disk_usage_gb"].rolling(7 * 24, min_periods=1).std()
df["qps_7d_std"] = df["qps"].rolling(7 * 24, min_periods=1).std()
return df
3.2 容量预测模型
from typing import Optional
@dataclass
class PredictionResult:
"""预测结果"""
timestamp: datetime
predicted_value: float
lower_80: float # 80% 置信区间下界
upper_80: float # 80% 置信区间上界
lower_95: float # 95% 置信区间下界
upper_95: float # 95% 置信区间上界
class CapacityPredictor:
"""容量预测器:基于趋势外推 + 季节性分解"""
def predict_disk_usage(
self,
history: pd.DataFrame,
horizon_days: int = 30,
capacity_limit_gb: float = 10000,
) -> list[PredictionResult]:
"""预测磁盘使用量"""
series = history["disk_usage_gb"].dropna()
if len(series) < 14 * 24: # 至少 14 天的小时级数据
raise ValueError("历史数据不足,至少需要 14 天")
# 趋势外推:线性回归
x = np.arange(len(series))
slope, intercept = np.polyfit(x, series.values, 1)
# 季节性分解:计算每小时的平均偏差
hourly_pattern = self._compute_hourly_pattern(series)
# 生成预测
predictions = []
last_ts = series.index[-1]
for i in range(1, horizon_days * 24 + 1):
future_ts = last_ts + timedelta(hours=i)
trend_value = intercept + slope * (len(series) + i)
seasonal_offset = hourly_pattern.get(future_ts.hour, 0)
predicted = trend_value + seasonal_offset
# 置信区间:基于残差的标准差
residuals = series.values – (intercept + slope * x)
std_residual = np.std(residuals)
# 预测越远,不确定性越大
uncertainty_factor = 1 + 0.02 * (i / 24) # 每天增加 2% 不确定性
predictions.append(PredictionResult(
timestamp=future_ts,
predicted_value=max(0, predicted),
lower_80=max(0, predicted – 1.28 * std_residual * uncertainty_factor),
upper_80=predicted + 1.28 * std_residual * uncertainty_factor,
lower_95=max(0, predicted – 1.96 * std_residual * uncertainty_factor),
upper_95=predicted + 1.96 * std_residual * uncertainty_factor,
))
return predictions
@staticmethod
def _compute_hourly_pattern(series: pd.Series) -> dict[int, float]:
"""计算每小时的平均季节性偏差"""
hourly_avg = series.groupby(series.index.hour).mean()
overall_avg = series.mean()
return {hour: avg – overall_avg for hour, avg in hourly_avg.items()}
def estimate_time_to_capacity(
self,
predictions: list[PredictionResult],
threshold_ratio: float = 0.85,
capacity_gb: float = 10000,
) -> Optional[datetime]:
"""估算到达容量阈值的时间"""
threshold_gb = capacity_gb * threshold_ratio
for pred in predictions:
if pred.upper_95 >= threshold_gb:
return pred.timestamp
return None
3.3 扩缩容决策引擎
@dataclass
class ScalingDecision:
"""扩缩容决策"""
action: str # scale_up / scale_down / hold
resource_type: str # disk / cpu / memory
current_value: float
predicted_peak: float
recommended_value: float
urgency: str # immediate / planned / low
cost_impact: float # 月度成本变化
reasoning: str
class CapacityPlanner:
"""容量规划决策引擎"""
def __init__(
self,
disk_per_node_gb: float = 2000,
cpu_per_node: int = 32,
memory_per_node_gb: float = 128,
cost_per_node_monthly: float = 5000,
):
self.disk_per_node = disk_per_node_gb
self.cpu_per_node = cpu_per_node
self.memory_per_node = memory_per_node_gb
self.cost_per_node = cost_per_node_monthly
def plan_disk_scaling(
self,
current_usage_gb: float,
total_capacity_gb: float,
predictions: list[PredictionResult],
safety_margin: float = 0.15,
) -> ScalingDecision:
"""磁盘扩缩容决策"""
usage_ratio = current_usage_gb / total_capacity_gb
# 预测 30 天内的峰值
predicted_peak = max(p.upper_95 for p in predictions[:30 * 24])
predicted_peak_ratio = predicted_peak / total_capacity_gb
# 安全阈值
threshold = 1.0 – safety_margin
if predicted_peak_ratio > threshold:
# 需要扩容
needed_capacity = predicted_peak / (1 – safety_margin)
additional_gb = needed_capacity – total_capacity_gb
additional_nodes = int(np.ceil(additional_gb / self.disk_per_node))
return ScalingDecision(
action="scale_up",
resource_type="disk",
current_value=total_capacity_gb,
predicted_peak=predicted_peak,
recommended_value=total_capacity_gb + additional_nodes * self.disk_per_node,
urgency="immediate" if usage_ratio > 0.80 else "planned",
cost_impact=additional_nodes * self.cost_per_node,
reasoning=(
f"当前使用率 {usage_ratio:.1%},30 天内预测峰值 "
f"{predicted_peak_ratio:.1%}(95% 置信区间上界),"
f"超过安全阈值 {threshold:.0%}。"
f"建议增加 {additional_nodes} 个节点。"
),
)
elif usage_ratio < 0.40 and predicted_peak_ratio < 0.50:
# 可以缩容
excess_gb = total_capacity_gb * 0.6 – predicted_peak
if excess_gb > self.disk_per_node:
remove_nodes = int(excess_gb // self.disk_per_node)
return ScalingDecision(
action="scale_down",
resource_type="disk",
current_value=total_capacity_gb,
predicted_peak=predicted_peak,
recommended_value=total_capacity_gb – remove_nodes * self.disk_per_node,
urgency="low",
cost_impact=-remove_nodes * self.cost_per_node,
reasoning=(
f"当前使用率 {usage_ratio:.1%},预测峰值 "
f"{predicted_peak_ratio:.1%},资源利用率低。"
f"可释放 {remove_nodes} 个节点。"
),
)
return ScalingDecision(
action="hold",
resource_type="disk",
current_value=total_capacity_gb,
predicted_peak=predicted_peak,
recommended_value=total_capacity_gb,
urgency="low",
cost_impact=0,
reasoning=f"当前使用率 {usage_ratio:.1%},预测峰值 {predicted_peak_ratio:.1%},容量充足。",
)
四、AI 容量规划的边界与架构权衡
预测模型的时效性:负载模式会随业务变化而改变(新功能上线、用户增长加速),历史数据训练的模型可能不再适用。建议每周重新训练模型,并用最近 7 天的实际数据验证预测准确度。如果 MAPE(平均绝对百分比误差)超过 20%,需要人工介入分析原因。
置信区间的决策含义:95% 置信区间的上界是"最坏情况"的估计。如果按上界规划,资源利用率可能偏低(浪费);如果按均值规划,可能应对不了突发。建议对核心存储(主库、关键业务表)按 95% 上界规划,对非核心存储按均值规划。
缩容的风险不对称性:扩容是"宁可多不可少"(多花钱 vs 宕机),缩容是"宁可少不可多"(省一点钱 vs 容量不足)。缩容决策的阈值应该更保守——只有当预测峰值远低于当前容量的 50% 时才考虑缩容,且缩容后仍需保留 30% 的安全余量。
业务活动的不可预测性:大促、新功能上线等业务活动的负载模式与日常不同,历史数据中可能没有类似模式。建议建立"活动日历",在预测模型中作为外部特征输入。没有活动日历时,预测模型无法预知突发流量。
五、总结
AI 容量规划的核心是将"拍脑袋"变成"看数据"——基于历史负载数据训练预测模型,输出带置信区间的容量预测,自动生成扩缩容方案。本文的关键实践为:用时间/周期/趋势特征工程提取负载模式、用趋势外推 + 季节性分解预测磁盘使用量、用置信区间上界做保守决策、用风险不对称性指导扩缩容阈值。预测不是万能的——业务活动的不可预测性和负载模式的变化是主要局限,人工判断仍是最终决策的关键。

