对 Azure Cosmos DB 请求速率过大 (429) 异常进行诊断及故障排除
适用范围: NoSQL
本文包含 API for NoSQL 的各种 429 状态代码错误的已知原因和解决方案。 如果你使用的是 API for MongoDB,请参阅排查 API for MongoDB 中的常见问题一文来了解如何调试状态代码 16500。
“请求速率过大”异常(也称为“错误代码 429”)表示,针对 Azure Cosmos DB 提出请求时,请求速率受限。
若要使用预配吞吐量,可以工作负载所需的每秒请求单位 (RU/s) 为单位设置吞吐量。 针对服务的数据库操作(如读取、写入和查询)会消耗一定数量的请求单元 (RU)。 详细了解请求单元。
在某一秒中,如果操作消耗的请求单位数超过预配数,Azure Cosmos DB 将返回 429 异常。 每秒重置可用的请求单位数。
在采取措施更改 RU/s 之前,必须了解速率受限的根本原因并解决根本问题。
提示
本文中的指导适用于使用预配置吞吐量(自动缩放和手动吞吐量)的数据库和容器。
存在对应不同类型的 429 异常的不同错误消息:
请求速率太大
这是最常见的情况。 当数据操作消耗的请求单位数超过预配的 RU/s 数时,即会发生这种情况。 如果使用手动吞吐量,则当每秒消耗的 RU 数多于预配的手动吞吐量时,就会发生这种情况。 如果使用自动缩放,则当消耗的 RU 数超过预配的最大每秒 RU 数时,就会发生这种情况。 例如,如果为某个资源预配的手动吞吐量为 400 RU/秒,则当一秒内消耗的请求单位数超过 400 个时,将会出现 429 错误。 如果为某个资源预配的自动缩放最大每秒 RU 数为 4000 RU/秒(在 400 RU/秒与 4000 RU/秒之间缩放),则当一秒内消耗的请求单位数超过 4000 个时,将会出现 429 响应错误。
第 1 步:检查指标,确定出现 429 错误的请求百分比
看到 429 错误消息并不一定表示数据库或容器存在问题。 无论是使用手动吞吐量还是自动缩放吞吐量,出现一小部分 429 响应错误都属正常情况,这表示即将达到预配的最大每秒 RU 数。
调查方式
与成功请求的总计数进行比较,确定出现 429 响应错误的数据库或容器请求所占的百分比。 从“Azure Cosmos DB 帐户”中,导航到“见解”>“请求”>“按状态代码显示的请求总数”。 筛选到特定数据库和容器。
默认情况下,Azure Cosmos DB 客户端 SDK 和数据导入工具(如 Azure 数据工厂和批量执行工具库)会自动重试提交出现 429 错误的请求。 重试次数通常最多为九次。 因此,虽然指标中可能会显示 429 响应错误,但这些错误甚至可能尚未返回到应用程序。
建议的解决方案
一般情况下,对于生产工作负载而言,如果看到出现 429 响应错误的请求百分比在 1% - 5% 之间,并且端到端延迟可接受,那么这就是一个可以表明 RU/s 受到充分利用的良好迹象。 不需要执行任何操作。 否则,请继续执行接下来的故障排除步骤。
重要
此 1-5% 范围的假设前提是你的帐户分区分布均匀。 如果你的分区分布不均匀,则问题分区可能会返回大量 429 错误,而总体速率可能较低。
如果使用自动缩放,即使 RU/秒值未扩展到最大 RU/秒,也可能会在数据库或容器上看到 429 响应错误。 有关说明,请参阅使用自动缩放时请求速率过大部分。
客户提出的一个常见问题是,“为何我在 Azure Monitor 指标中看到了 429 响应错误,但在我自己的应用程序监视中却没有看到?”如果 Azure Monitor 指标显示了 429 响应错误,但你在自己的应用程序中未看到这种错误,则原因是默认情况下,Azure Cosmos DB 客户端 SDK automatically retried internally on the 429 responses
和请求在后续的重试中已成功。 因此,不会向应用程序返回 429 状态代码。 在这种情况下,429 响应错误的总体比率通常最小(假设总体比率为 1-5%),可以放心地忽略,并且端到端延迟对于应用程序是可接受的。
步骤 2:确定是否存在热分区
当一个或几个逻辑分区键由于请求量较高而消耗了过多的总 RU/s 时,即会出现热分区。 而造成这一情况的原因可能是分区键设计导致请求未能均匀分布。 这会导致许多请求被定向到变“热”的一小部分逻辑(这里表示物理)分区。由于逻辑分区的所有数据都驻留在一个物理分区上,并且总 RU/s 在物理分区之间均匀分布,因此,热分区可能导致吞吐量的 429 响应和低效使用。
下面是导致热分区的一些分区策略示例:
- 你有一个用于存储 IoT 设备写入密集型工作负载数据的容器,并按
date
进行分区。 单个日期的所有数据将驻留在同一逻辑分区和物理分区中。 由于每天写入的所有数据的日期都相同,因此会导致每天都产生热分区。- 对于这种情况,使用
id
等分区键(GUID 或设备 ID)或是组合id
和date
的合成分区键反而将生成更高的值基数,并更均匀地分布请求量。
- 对于这种情况,使用
- 你有一个多租户方案,使用的容器按
tenantId
进行分区。 如果一个租户的活跃性明显高于其他租户,则会导致热分区。 举例来说,如果最大租户有 100,000 名用户,但大多数租户的用户数少于 10 人,则按tenantID
分区时,将会产生热分区。- 对于上述方案,应考虑为最大租户创建专用容器,并按更精细的属性(如
UserId
)进行分区。
- 对于上述方案,应考虑为最大租户创建专用容器,并按更精细的属性(如
如何识别热分区
若要验证是否存在热分区,请导航到“见解”>“吞吐量”>“按 PartitionKeyRangeID 列出的规范化 RU 消耗量(%)”。 筛选到特定数据库和容器。
每个分区键范围 ID 映射到一个物理分区。 如果某个 PartitionKeyRangeId 的规范化 RU 消耗量明显高于其他 PartitionKeyRangeId(例如,一个 PartitionKeyRangeId 的消耗量一直为 100%,而其他 PartitionKeyRangeId 的消耗量不超过 30%),则可能表示存在热分区。 详细了解规范化 RU 消耗量指标。
如需了解消耗 RU/s 最多的逻辑分区键,请查阅 Azure 诊断日志。 此示例查询汇总了每个逻辑分区键每秒消耗的请求单位总数。
重要
启用诊断日志将单独产生 Log Analytics 服务费(按引入的数据量计费)。 建议出于调试目的启用诊断日志有限的一段时间,在不需要诊断日志时将其禁用。 请参阅定价页面了解详细信息。
CDBPartitionKeyRUConsumption
| where TimeGenerated >= ago(24hour)
| where CollectionName == "CollectionName"
| where isnotempty(PartitionKey)
// Sum total request units consumed by logical partition key for each second
| summarize sum(RequestCharge) by PartitionKey, OperationName, bin(TimeGenerated, 1s)
| order by sum_RequestCharge desc
此示例输出显示,在某一分钟内,值为“Contoso”的逻辑分区键的消耗量约为 12,000 RU/s,而值为“Fabrikam”的逻辑分区键的消耗量则少于 600 RU/s。 如果此模式在速率受限发生期间一直存在,则表明存在热分区。
提示
在任何工作负载中,跨逻辑分区的请求量都存在自然变化。 你应当确定导致产生热分区的原因是因分区键选择造成的基本偏斜(可能需要更改分区键)还是因工作负荷模式的自然变化而暂时出现峰值。
建议的解决方案
查看有关如何选择良好分区键的指导。
如果受到速率限制的请求百分比较高,但未出现热分区:
- 可以使用客户端 SDK、Azure 门户、PowerShell、CLI 或 ARM 模板来增加数据库或容器的 RU/s。 按照缩放预配吞吐量(RU/秒)的最佳做法,确定要设置的正确 RU/秒。
如果受到速率限制的请求百分比较高,并出现了根本性的热分区:
- 从长期来看,为获得最佳成本效益和性能,应考虑更改分区键。 无法就地更新分区键,因此,这需要将数据迁移到具有不同分区键的新容器。 Azure Cosmos DB 支持使用实时数据迁移工具来实现此目的。
- 短期而言,你可以暂时增大资源的总体 RU/秒,以提高热分区的吞吐量。 不建议将此用作长期策略,因为它会导致每秒 RU 数过度预配和增加成本。
提示
增加吞吐量时,纵向扩展操作可能会即时或最多需要 5-6 小时完成,具体视要纵向扩展的 RU/s 数量而定。 若要了解在不触发异步纵向扩展操作(需要使用 Azure Cosmos DB 预配更多物理分区)的情况下,可以设置的最高 RU/s 数,请将不同的分区键范围 ID 乘以 100,000 RU/s。 例如,如果已预配 30,000 RU/s 及 5 个物理分区(每个物理分区已分配 6,000 RU/s),则可以在即时纵向扩展操作中将 RU/s 数增至 50,000 RU/s(每个物理分区 10,000 RU/s)。 但若要将 RU/s 数增至 50,000 RU/s 以上,则需要执行异步纵向扩展操作。 在有关缩放预配吞吐量(RU/秒)的最佳做法中了解详细信息。
步骤 3:确定返回 429 响应错误的请求
如何调查出现 429 响应错误的请求
使用 Azure 诊断日志来确定返回 429 响应错误的请求,以及这些请求消耗的 RU 数。 此示例查询按分钟聚合。
重要
启用诊断日志将单独产生 Log Analytics 服务费(按引入的数据量计费)。 我们建议你在有限时间内启用诊断日志进行调试;若不再需要时,建议关闭诊断日志。 请参阅定价页面了解详细信息。
CDBDataPlaneRequests
| where TimeGenerated >= ago(24h)
| summarize throttledOperations = dcountif(ActivityId, StatusCode == 429), totalOperations = dcount(ActivityId), totalConsumedRUPerMinute = sum(RequestCharge) by DatabaseName, CollectionName, OperationName, RequestResourceType, bin(TimeGenerated, 1min)
| extend averageRUPerOperation = 1.0 * totalConsumedRUPerMinute / totalOperations
| extend fractionOf429s = 1.0 * throttledOperations / totalOperations
| order by fractionOf429s desc
例如,此示例输出显示,每分钟有 30% 的创建文档请求的速率受到限制,并且每个请求平均消耗 17 个请求单位。
建议的解决方案
使用 Azure Cosmos DB 容量规划器
可以使用 Azure Cosmos DB 容量规划器,了解什么是基于工作负载(操作量和类型以及文档大小)的最佳预配吞吐量。 可以通过提供示例数据来进一步自定义计算,以获得更准确的估计。
创建、替换或更新插入文档请求时出现 429 响应错误
- 默认情况下,API for NoSQL 会默认对所有属性都编制索引。 你可以调整索引策略,以仅对所需属性编制索引。 这将减少每个创建文档操作所需的请求单位数,从而降低看到 429 响应错误的可能性,或者允许你在使用相同数量预配 RU/s 的情况下,每秒执行更多操作。
查询文档请求时出现 429 响应错误
- 遵循对 RU 费用较高的查询进行故障排除的相关指导
执行存储过程时出现 429 响应错误
- 存储过程适用于需要跨分区键值写入事务的操作。 不建议将存储过程用于大量的读取或查询操作。 要获得最佳性能,应在客户端使用 Azure Cosmos DB SDK 完成这些读取或查询操作。
使用自动缩放时请求速率过大
本文中的所有指导适用于手动吞吐量和自动缩放吞吐量。
使用自动缩放时,客户提出的一个常见问题是,“使用自动缩放时是否仍会出现 429 响应错误?”
是的。 此错误主要发生在两种场景中。
场景 1:当总体消耗的 RU/秒超过数据库或容器的最大 RU/秒时,服务将相应地限制请求。 这类似于超过了数据库或容器的总体手动预配吞吐量。
场景 2:如果有一个热分区(即逻辑分区键值,该键值的请求数与其他分区键值的请求数相比过高),则基础物理分区可能超出其 RU/秒预算。 为避免出现热分区,最佳做法是选择良好的分区键,从而实现存储和吞吐量的均匀分布。 这类似于使用手动吞吐量时存在热分区的情况。
例如,如果选择“20,000 RU/秒最大吞吐量”选项,并且 200 GB 的存储空间包含 4 个物理分区,则每个物理分区最多可以自动扩展到 5000 RU/秒。 如果某个特定逻辑分区键上存在热分区,则当它所在的基础物理分区超过 5000 RU/秒(即超出了 100% 的标准化利用率)时,将会出现 429 响应错误。
按照步骤 1、步骤 2 和步骤 3 中的指导针对这些场景进行调试。
另一个常见问题是,“为何规范化 RU 消耗量 100%,但自动缩放未扩展到最大 RU/秒?”
这种情况通常发生在临时或间歇性达到使用量高峰的工作负载上。 使用自动缩放时,仅当规范化 RU 消耗量在 5 秒间隔内持续稳定地保持 100% 时,Azure Cosmos DB 才会将 RU/秒扩展到最大吞吐量。 这是为了确保缩放逻辑的成本对用户可接受,避免单次短暂性的高峰导致不必要的扩展和提高成本。 在出现短暂性的高峰时,系统通常会扩展到一个比上次所扩展到的 RU/秒更高,但比最大 RU/秒更低的值。 详细了解如何解读使用自动缩放时的规范化 RU 消耗量指标。
元数据请求速率受限
对数据库和/或容器执行大量元数据操作时,可能会发生元数据速率受限问题。 元数据操作包括:
- 创建、读取、更新或删除容器或数据库
- 列出 Azure Cosmos DB 帐户中的数据库或容器
- 查询产品/服务,以查看当前预配的吞吐量
这些操作存在系统保留的 RU 限制,因此增大数据库或容器的预配每秒 RU 数没有任何效果,我们也不建议这样做。 请参阅控制平面服务限制。
调查方式
导航到“见解”>“系统”>“按状态代码显示的元数据请求”。 根据需要,筛选到特定数据库和容器。
建议的解决方案
如果应用程序需要执行元数据操作,请考虑实施回退策略以降低该等请求的发送速率。
使用静态 Azure Cosmos DB 客户端实例。 初始化 DocumentClient 或 CosmosClient 时,Azure Cosmos DB SDK 将提取有关该帐户的元数据,包括与一致性级别、数据库、容器、分区以及产品/服务相关的信息。 此初始化可能需要使用大量 RU,不应频繁执行。 在应用程序的生存期内使用单个 DocumentClient 实例。
缓存数据库和容器名称。 从配置中检索数据库和容器的名称,或者一开始就将其缓存。 ReadDatabaseAsync/ReadDocumentCollectionAsync 或 CreateDatabaseQuery/CreateDocumentCollectionQuery 等调用将导致元数据调用相关服务,而该等调用将会消耗系统保留的有限 RU。 因此,你应该避免频繁执行此类操作。
因暂时性服务错误而导致速率受限
当请求遇到暂时性服务错误时,系统将返回此 429 错误。 增大数据库或容器的每秒 RU 数没有任何效果,不建议这样做。
建议的解决方案
重试请求。 如果数分钟后错误仍然存在,请从 Azure 门户提交支持票证。
后续步骤
- 监视数据库或容器的规范化 RU/s 消耗量。
- 诊断和排查在使用 Azure Cosmos DB .NET SDK 时遇到的问题。
- 了解 .NET v3 和 .NET v2 的性能准则。
- 诊断和排查使用 Azure Cosmos DB Java v4 SDK 时遇到的问题。
- 了解 Java v4 SDK 的性能准则。