DynamoDB是AWS推出的全托管NoSQL数据库服务,用户不需要操心服务器运维,但它有一套自己的资源管控方式,这就是吞吐容量限制机制。不少团队在业务量上涨后发现请求莫名其妙被拒绝,日志里出现ProvisionedThroughputExceededException错误,却不知道原因在哪里。理解DynamoDB的API调用频率限制原理,是用好这项服务的关键一步。

一、DynamoDB限流的基本原理:分区与容量单位
DynamoDB底层将表的数据按照分区键的哈希值切分到多个分区(Partition)上,每个分区都拥有独立的读写容量上限。限流并不是针对整张表的一个简单计数器,而是发生在分区级别的。这就解释了一个常见现象:明明整张表的配置容量足够,但所有请求都集中打在某一个分区键上时,依然会被限流,这就是所谓的热点分区问题。
DynamoDB使用两种容量单位来度量吞吐量。读容量单位RCU表示对最多4KB大小的条目执行一次强一致读所需的容量,最终一致读只消耗一半的RCU;写容量单位WCU表示对最多1KB大小的条目执行一次写入所需的容量。如果条目超过了这些尺寸,容量消耗会按倍数增加。例如一个6KB条目的写入需要消耗6个WCU,一次强一致读则需要消耗2个RCU。
预置容量模式下,DynamoDB还会通过令牌桶算法允许短暂的突发流量。系统会预留一部分未使用的容量,最多可以积累到300秒的量,让短时间的流量尖峰不至于立刻被拒。但一旦突发额度耗尽,后续请求就会被限流,直到额度恢复。这个机制容易被忽视,很多压测时表现正常的系统,上线后因为突发额度提前耗尽而频繁报错。
二、按需模式与预置模式的差异
DynamoDB提供两种容量计费模式。预置模式(Provisioned)要求事先设定表的读写容量,适合流量平稳可预测的业务,成本相对可控,但需要配合自动扩缩容策略。按需模式(On-Demand)则无需任何容量设置,DynamoDB会根据实际请求量自动适配,几乎不会出现容量不足导致的限流。
听起来按需模式似乎完美解决问题,但它也有代价。按需模式的单次读写价格大约是预置模式的数倍,对于流量稳定的大规模业务,长期使用按需模式成本会明显偏高。一个常见的实践是:新业务或流量波动的业务先用按需模式快速上线,等流量模式稳定后再切换回预置模式并配合Auto Scaling来优化成本。需要注意的是,两种模式之间每24小时才能切换一次,切换前要评估清楚。
即便是按需模式,也并非完全没有上限。DynamoDB对每张表的按需吞吐设定了软性限额,例如每个分区每秒大约可以支持1000 WCU和3000 RCU。超出这个量级的请求依然可能被限流,只是DynamoDB会自动增加分区来横向扩展。极端热点键场景下,单个分区键的写入压力无论哪种模式都难以突破,这需要从数据建模层面解决。
三、被限流后的正确应对策略
客户端遇到限流时,最直接的措施是实现重试机制。AWS官方SDK默认内置了针对限流错误的指数退避重试,但没有内置重试的开发者需要自己实现。以下是一个典型的指数退避重试伪代码示例:
public void putWithRetry(DynamoDbClient client, PutItemRequest request) {
int maxRetries = 5;
long backoff = 50; // 初始退避50毫秒
for (int i = 0; i <= maxRetries; i++) {
try {
client.putItem(request);
return;
} catch (ProvisionedThroughputExceededException e) {
if (i == maxRetries) {
throw new RuntimeException("重试次数耗尽", e);
}
long sleep = backoff + (long)(Math.random() * backoff);
try {
Thread.sleep(sleep);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException(ie);
}
backoff *= 2; // 指数增长退避时间
}
}
}重试只能缓解症状,根治问题还要从数据建模入手。对于热点分区键,常用的手段是加随机后缀打散:在分区键后面追加0到N之间的随机数,将写入压力分散到N个逻辑分片上,读取时再分别查询所有分片合并结果。此外还可以考虑批量写入接口BatchWriteItem,它把最多25个写入请求合并为一次API调用,减少请求次数的同时也降低了被限流的概率。
另一个容易踩的坑是条目尺寸过大。一个100KB的条目写入会瞬间消耗100个WCU,如果这类写入频繁出现,容量会很快耗尽。设计表结构时应尽量把大对象拆分到S3,DynamoDB中只存引用,同时避免不必要的属性存储。监控方面,建议开启CloudWatch中ConsumedReadCapacityUnits和ConsumedThrottledRequests等指标,配置告警,在用户感知到错误之前就发现容量瓶颈。
四、常见限流场景排查清单
排查限流问题时可以按以下顺序逐一检查:第一,确认表的容量模式是什么,预置模式下的配置值是否与实际流量匹配;第二,查看CloudWatch的ThrottledRequests指标,确认限流是全表性的还是集中在某些键上;第三,检查条目平均大小,超大条目会放大容量消耗;第四,检查是否存在局部二级索引或全局二级索引的容量不足,GSI的容量是独立配置的,很多限流其实发生在索引上而非主表;第五,确认重试逻辑是否已经启用并配置了合理的抖动因子,避免重试风暴加剧拥塞。
理解DynamoDB的限流机制,本质上是理解它的容量模型与分区架构。容量单位怎么计算、分区如何分裂、突发额度如何积累,这些细节决定了系统在高并发下的表现。选对容量模式、设计好分区键、实现健壮的重试逻辑,再配合完善的监控告警,就能让DynamoDB在业务增长中始终保持稳定可靠的表现。