Azure Billing Usage API 聚合功能无法正常工作
结论
aggregationGranularity=Daily 只说明用量记录按“天”划分时间区间,并不代表整个订阅每天只会返回一条记录。showDetails=false 也不会强制按 subscriptionId + meterId + usageStartTime + usageEndTime 进行唯一分组。
Azure Usage Aggregates API 还可能按资源实例、区域、计费属性等内部维度拆分记录。如果只检查上述四个字段,就可能看到貌似重复的数据。需要继续比较记录中的其他维度,再根据报表需求,在客户端或数据库中执行 GROUP BY 和 SUM(quantity)。
如果 showDetails=true 和 false 返回的完整记录完全一致,还要考虑这个 Preview API 可能没有按预期处理参数。此时不能依靠 showDetails 保证最终的汇总结果。
为什么会出现“重复记录”
aggregationGranularity=Daily 控制的是时间粒度。例如:
usageStartTime = 2016-03-02T00:00:00Z
usageEndTime = 2016-03-03T00:00:00Z
即使日期和 meterId 相同,也可能返回多条记录,因为它们可能对应不同的:
- 资源实例或资源 URI
- Resource Group
- Region
instanceDatainfoFields- 计费属性或用量来源
因此,下面四个字段通常不能作为唯一键:
subscriptionId
meterId
usageStartTime
usageEndTime
应比较完整响应,尤其是 instanceData、infoFields、quantity、记录 ID 和资源相关字段。
showDetails=false 用于请求较高层级的用量汇总或减少实例明细,但不等同于:
GROUP BY subscriptionId, meterId, usageStartTime, usageEndTime
服务端使用的计费维度可能比响应中直接显示的字段更多,这种情况在旧版 Preview 接口中尤其需要留意。
推荐处理方式
1. 使用规范的时间参数
参数名建议采用文档中的大小写,时间则使用标准 URL 编码:
GET https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Commerce/UsageAggregates
?api-version=2015-06-01-preview
&reportedStartTime=2016-03-02T00%3A00%3A00Z
&reportedEndTime=2016-03-03T00%3A00%3A00Z
&aggregationGranularity=Daily
&showDetails=false
时间应使用 UTC。日期范围最好按左闭右开处理。例如,查询 3 月 2 日的数据时使用:
[2016-03-02T00:00:00Z, 2016-03-03T00:00:00Z)
2. 检查完整记录差异
不要只比较那四个字段。可以同时保存或输出以下内容:
id
name
subscriptionId
meterId
usageStartTime
usageEndTime
quantity
instanceData
infoFields
如果 instanceData 或资源 URI 不同,就不是重复数据,而是不同资源产生的同类计量用量。
3. 按实际报表维度重新汇总
如果需要得到“每个订阅、每天、每个 meter 的总用量”,可以在数据库中进行汇总:
SELECT
subscription_id,
meter_id,
usage_start_time,
usage_end_time,
SUM(quantity) AS total_quantity
FROM azure_usage
GROUP BY
subscription_id,
meter_id,
usage_start_time,
usage_end_time;
如果不同记录可能使用不同的计量单位,还应把 unit 加入分组条件:
SELECT
subscription_id,
meter_id,
unit,
usage_start_time,
usage_end_time,
SUM(quantity) AS total_quantity
FROM azure_usage
GROUP BY
subscription_id,
meter_id,
unit,
usage_start_time,
usage_end_time;
不同 meterId 或不同单位的 quantity 不能直接相加。
4. 处理分页并保证写入幂等
Usage API 的响应可能带有 nextLink。请求后续页面时,应直接使用服务端返回的 nextLink,不要自行重新拼接查询参数。
写入数据库时,也不能只根据日期和 meterId 判断记录是否重复。可以保存 API 返回的记录 ID。如果接口没有合适的稳定标识,则应根据完整的业务维度生成幂等键。
例如:
var dailyUsage = records
.GroupBy(x => new
{
x.SubscriptionId,
x.MeterId,
x.Unit,
Day = x.UsageStartTime.Date
})
.Select(group => new
{
group.Key.SubscriptionId,
group.Key.MeterId,
group.Key.Unit,
group.Key.Day,
Quantity = group.Sum(x => x.Quantity)
})
.ToList();
如何判断是维度拆分还是异常重复
可以根据以下情况判断:
- 四个字段相同,但
instanceData、资源 URI 或其他属性不同:属于正常的维度拆分,可按实际需求汇总。 - 完整记录相同,但来自不同分页:检查是否重复请求了第一页,或者是否错误拼接了
nextLink。 - 完整记录相同,且来自同一次响应:可能是 Preview 服务的数据问题。应保存原始响应、请求时间和相关请求标识,再提交 Azure 支持工单。
showDetails=true与false的完整响应始终完全相同:不要把这个参数作为汇总正确性的唯一依据,应用端仍需明确执行聚合。
注意事项
2015-06-01-preview 是较旧的 Preview 接口。新系统应优先评估当前受支持的 Azure Consumption Usage Details API 或 Cost Management Query API,并结合订阅类型、计费账户类型和 Azure 官方文档选择接口。迁移前应重新确认字段含义、分页方式、时间范围和聚合维度,不能假设新旧接口的行为相同。
备注:内容仅供参考。
