PAYATHON 2026

使用 MonitorManagementClient 查询 UsedCapacity 为何偶发容量偏差?

数据阿明

结论

先检查数据点的时间戳和读取逻辑,再判断指标是否异常。Data.First() 取的是集合中的第一个数据点,未必代表最新容量。扩大查询时间范围有助于找到有效样本,但也需要调整数据点的选择方式。

仅凭现有代码,无法判断 324 GB 与 7 MB 的差异是由旧样本、比较口径还是指标上报异常造成的,也不能认定 SDK 存在缺陷。

UsedCapacity 的采样粒度和统计口径

Microsoft 的指标文档列出,UsedCapacity 的单位是 Bytes,聚合方式为 Average,支持的最小时间粒度是 PT1H,即一小时。对于标准存储账户,它统计 Blob、Table、File 和 Queue 使用容量的总和。指标定义

查询最近 60 分钟的数据时,应结合小时级采样粒度理解结果,不能直接把返回值视为查询瞬间的容量。Average 表示对应时间桶内的平均值,扩大查询范围也不会让它自动变成当前容量。

原代码还有两个读取风险:

  • 连续调用 First(),没有检查指标、时间序列或数据集合是否为空。
  • 直接访问 Average.Value,没有检查数据点是否包含有效值。Azure Monitor 中缺失数据与零值含义不同,不能把缺失数据当作容量为零。聚合与缺失值说明

修改查询与数据点选择方式

可以先将查询范围扩大到最近 24 小时,再按时间戳选择最新的有效数据点。24 小时是这里用于排查的示例,并非 Azure 要求的固定范围,也不保证最新样本一定及时可用。

下面沿用原有 SDK 调用方式,修改资源 ID、时间范围和结果读取逻辑:

using System;
using System.Linq;

var uri = $"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Storage/storageAccounts/{storageAccountName}";

var end = DateTime.UtcNow;
var start = end.AddHours(-24);
var time = $"{start:o}/{end:o}";

var monitorManagementClient =
    await this.monitorManagement.CreateAsync(
        subscriptionId, cancellationToken);

var response = await monitorManagementClient.Metrics.ListAsync(
    resourceUri: uri,
    timespan: time,
    metricnames: "UsedCapacity",
    aggregation: "Average",
    cancellationToken: cancellationToken);

var metric = response.Value?.FirstOrDefault(m =>
    string.Equals(
        m.Name?.Value,
        "UsedCapacity",
        StringComparison.OrdinalIgnoreCase));

if (metric?.Timeseries == null || metric.Timeseries.Count != 1)
{
    throw new InvalidOperationException(
        "Expected one UsedCapacity time series; inspect the response.");
}

var data = metric.Timeseries[0].Data;
if (data == null)
{
    throw new InvalidOperationException("No metric data returned.");
}

var latest = data
    .Where(p => p.Average.HasValue)
    .OrderByDescending(p => p.TimeStamp)
    .FirstOrDefault();

if (latest == null)
{
    throw new InvalidOperationException(
        "No valid UsedCapacity sample in the requested time range.");
}

var bytes = latest.Average.Value;
var gb = bytes / 1_000_000_000d;
var gib = bytes / (1024d * 1024d * 1024d);

Console.WriteLine(
    $"SampleTime={latest.TimeStamp:o}, " +
    $"Bytes={bytes}, GB={gb:F3}, GiB={gib:F3}");

返回的是查询范围内最新的有效样本。业务程序还应检查样本距离当前时间有多久,根据业务允许的延迟决定是否接受。样本过旧时,应显示采样时间或标记数据过期。

查询时也应核对响应中的实际时间粒度。需要显式指定一小时时间粒度时,REST API 使用 interval=PT1H;旧版预览 SDK 的对应参数及类型应以实际安装包的方法签名为准。Metrics API 查询说明

如何进一步定位 7 MB 的来源

  1. 记录所有返回数据点的时间戳、Average、指标单位、响应时间粒度和资源 ID,确认 7 MB 对应哪个时间桶,以及其他时间桶是否有接近 324 GB 的值。
  2. 在 Azure Portal 的 Metrics 中选择同一个存储账户、Used capacity、相同 UTC 时间范围、一小时粒度和 Average 聚合。若 324 GB 来自其他页面或自行汇总,先确认统计范围是否一致。
  3. 核对单位。API 返回字节,GB 与 GiB 使用不同的除数。这种换算差异不足以解释 324 GB 与 7 MB 的数量级差距,但仍需统一显示方式。
  4. 使用相同参数调用 Metrics REST API,交叉比较请求结果。如果原始响应也包含异常值,应继续检查指标数据;如果原始响应正常而程序显示异常,则检查 SDK 结果读取、换算和缓存逻辑。

如果相同资源、相同时间桶的原始指标仍明显异常,保留请求参数、响应、请求标识及发生时间,再向 Azure 支持反馈。现有材料没有证明 Microsoft.Azure.Management.Monitor 的 0.28.0-preview 存在导致此现象的已知问题,也不能承诺升级 SDK 就能解决。

备注:内容仅供参考。