我一直在测试将大型数据迁移到发电机,我们打算在今年夏天在产品帐户中进行该测试。我运行了一个测试,将大约32亿个文档批量写入到我们的dynamo表中,该表具有一个哈希和范围键以及两个部分索引。每个文档很小,小于1k。虽然我们成功地在大约3天的时间内完成了项目的编写,但我们对Dynamo的性能感到失望,并在寻求有关如何改进方面的建议。
为了进行此迁移,我们使用了2个ec2实例(c4.8xlarges)。每个程序最多运行10个迁移程序流程;我们通过一些内部参数将工作划分为多个流程,并且知道某些流程的运行时间会比其他流程更长。每个过程都会查询我们的RDS数据库以获取100,000条记录。然后,我们将它们分成25个分区,并使用10个线程的线程池来调用DynamoDB Java SDK的batchSave()方法。每次对batchSave()的调用仅发送25个文档,每个文档少于1k,因此我们希望每个文档仅对AWS进行一次HTTP调用。这意味着在任何给定时间,我们服务器上最多可以有100个线程,每个线程用25条记录调用batchSave。在这段时间内,我们的RDS实例可以很好地处理查询负载,而我们的2个EC2实例也可以。在ec2方面,我们没有最大化CPU,内存或网络输入或网络输出。我们的写入未按哈希键分组,因为我们知道可以减慢发电机写入速度。通常,在一组100,000条记录中,它们分为88,000个不同的哈希键。我最初创建的dynamo表的写入吞吐量为30,000,但在测试过程中的某一时刻最多配置了40,000的写入吞吐量,因此我们的理解是,在dynamo侧至少有40个分区来处理此问题。
在此期间,我们在对dynamo的batchSave()调用中看到的响应时间变化很大。在每个ec2实例运行100个线程的20分钟内,平均时间为0.636秒,但中位数仅为0.374,因此,许多呼叫花费的时间超过一秒。我希望在将这些调用从EC2实例传递到发电机时所花费的时间更加一致。我们的dynamo表似乎配置了足够的吞吐量,并且EC2实例的CPU占用率低于10%,并且网络的进出状态看起来很正常,但还没有接近最大化。控制台中的CloudWatch图(相当糟糕...)没有显示任何写请求限制。
经过这些采样时间后,我们的某些流程完成了它们的工作,因此我们在ec2实例上运行的线程更少。发生这种情况时,我们看到了对发电机的响应时间大大缩短了。例如当我们在ec2实例上运行40个线程而不是100个线程时,每个线程都调用batchSave,响应时间提高了5倍以上。但是,即使增加了更好的响应时间,我们也没有看到写入吞吐量的提高。看来,无论我们将写吞吐量配置为什么,我们都从未真正看到实际吞吐量超过15,000。
我们想要一些有关如何在这样的Dynamo迁移上更好地实现更好性能的建议。当然,今年夏天我们的生产迁移将对时间敏感,届时,我们将寻求迁移约40亿条记录。是否有人对我们如何实现整体更高的吞吐率有任何建议?如果我们愿意在迁移过程中为主要索引支付30,000个单位的写入吞吐量,那么我们如何才能真正实现接近该水平的性能?
最佳答案
BatchWrite延迟的一个组成部分是Put请求,该请求在Batch中花费的时间最长。考虑到您必须遍历DynamoDBMapper.FailedBatch的列表,直到它为空为止,您的进度可能不够快。考虑运行多个并行的DynamoDBMapper.save()调用,而不是运行batchSave,这样您就可以为编写的每个项目独立地取得进展。
同样,Cloudwatch指标是1分钟指标,因此您可能会有1分钟窗口掩盖的消耗量和节流量峰值。由于默认情况下,SDK会在向客户端公开ProvisionedThroughputExceededException之前,默认情况下重试受限制的调用10 times,从而使查明实际限制的时间和地点变得困难。为了提高您的理解,请尝试减少SDK重试的次数,请求ConsumedCapacity = TOTAL,按照rate-limited scan blog post中的描述使用Guava RateLimiter对您的写进行自我限制,并记录受限制的主键以查看是否出现任何模式。
最后,表的分区数不仅取决于您在表上配置的读写容量单位的数量。它也取决于您存储在表中的数据量。通常,一个分区最多可存储10GB的数据,然后再拆分。因此,如果只写表而不删除旧条目,表中的分区数将无限增加。这将导致IOPS短缺-即使您提供40000 WCU / s,如果由于数据量而已经有80个分区,则40k WCU将在80个分区之间分配,平均每个分区500 WCU。要控制表中的过时数据量,您可以执行速率受限的清除过程,以扫描并删除旧条目,或者使用rolling time-series tables(幻灯片84-95)并删除/迁移不那么相关的整个数据表。滚动时序表比速率限制的清理便宜,因为您不使用DeleteTable操作消耗WCU,而每个DeleteItem调用至少消耗1个WCU。