更改DynamoDB表的模式:最佳/推荐的方式是什么?

72
什么是在生产DynamoDB中更改大表模式的Amazon推荐方法?
假设我们有一个名为Person的表,具有主哈希键SSN,该表可能包含1000万个项目。现在,由于身份盗窃数量的严重性,这个假设国家的政府引入了另一种个人识别:Unique Personal Identifier(UPI)。 我们必须添加一个UPI列并更改Person表的模式,以便现在的主哈希键是UPI。我们想要同时支持使用SSN和使用UPI的当前系统和新系统,因此需要使这两列共存于Person表中。 Amazon推荐的方法是什么来完成这个模式变化?
4个回答

40
有几种方法可供选择,但首先需要了解无法更改现有表的架构。要获得不同的架构,必须创建一个新表格。您可能能够重用现有表格,但结果与创建不同的表格相同。
  1. 向同一表格进行惰性迁移,不使用Streams。每次修改Person表中的条目时,请使用UPI而不是SSN作为哈希键的值,在Person表中创建一个新项目,并删除以SSN为键的旧项目。这假定UPI从与SSN不同的值范围中绘制。如果SSN看起来像XXX-XX-XXXX,那么只要UPI的位数与SSN不同,您就永远不会发生重叠。
  2. 向同一表格进行惰性迁移,使用Streams。当流变得普遍可用时,您将能够为您的Person表打开流。使用NEW_AND_OLD_IMAGES流视图类型创建一个流,每当检测到更改向Person表中现有人员添加UPI的条目时,请创建一个Lambda函数,该函数删除以SSN为键的人员,并添加具有相同属性的人员以UPI为键。此方法存在竞态条件,可以通过向项添加原子计数器版本属性并将DeleteItem调用取决于版本属性来减轻竞态条件。
  3. 使用Streams进行预防性(脚本化)迁移到不同的表格。运行一个脚本,扫描您的表格,并向Person表格中的每个Person-item添加唯一的UPI。使用NEW_AND_OLD_IMAGES流视图类型在Person表上创建一个流,并订阅lambda函数到该流,当lambda函数检测到具有UPI的人员发生更改或添加UPI的人员时,将所有新的Persons写入新的Person_UPI表。基表上的变异通常需要几百毫秒才能出现在流中作为流记录,因此您可以在应用程序中对新的Person_UPI表进行热故障转移。拒绝请求几秒钟,在此期间将应用程序指向Person_UPI表,并重新启用请求。

感谢提供的解决方案。如何处理其他表中包含个人社会安全号码作为其属性值之一的外键?我需要在一个原子操作中更新所有这些,否则可能会出现竞争条件。据我所知,在DynamoDB中没有官方事务机制 - 我偶然发现了一个事务库,但它是用Java编写的,并且据说落后于最新版本的DynamoDB。是否有任何事务机制可以在Python中使用(使用Boto)? - Dimitre Novatchev
据我所知,事务库仅在Java中可用。 - Alexander Patrikalakis
在你的有益回答中,为什么要介绍ITIN标签?这让我感到困惑。我不得不去查找它的含义。当然,SSN和ITIN是现实世界中的首字母缩略词,但请坚持使用问题中使用的标签。 - osullic
2
固定 ITIN -> SSN - Alexander Patrikalakis

28
DynamoDB 流使我们能够在没有任何停机时间的情况下迁移表。我已经成功完成了这项工作,遵循的步骤如下:
  1. 创建一个新表(让我们称之为 NewTable),具有所需的键结构、LSIs 和 GSIs。
  2. 在原始表上启用 DynamoDB 流
  3. 将 Lambda 关联到 Stream,将记录推送到 NewTable 中。(该 Lambda 应该将第 5 步中的迁移标志裁剪掉)
  4. [可选] 在原始表上创建 GSI 以加速扫描项。确保此 GSI 只具有属性:主键和已迁移(请参见第 5 步)。
  5. 扫描上一步创建的 GSI(或整个表)并使用以下筛选器:

    FilterExpression = "attribute_not_exists(Migrated)"

更新表中的每个项目,标记为已迁移(例如:“Migrated”:{“S”:“0”}),并使用 UpdateItem API 将其发送到 DynamoDB 流,以确保不会发生数据丢失。

注意:您可能需要在更新期间增加表的写入容量单位。

  1. Lambda 将拾取所有项目,删除 Migrated 标记并将其推送到 NewTable 中。
  2. 完成所有项目的迁移后,将代码重定向到新表
  3. 删除原始表和 Lambda 函数,一旦满意就可以删除它们。

按照这些步骤操作应该能够确保您没有数据丢失和停机时间。

我已经在我的博客上记录了这篇文章,并提供了代码: https://www.abhayachauhan.com/2018/01/dynamodb-changing-table-schema/


5
你好,你能更新提供的链接吗?它已经失效了。无论如何感谢你的回答!! - ielkhalloufi
10
哇,与关系型数据库或MongoDB中的迁移相比,上述内容需要大量的专业机制来完成在其他持久性框架中非常简单的事情。 顺便问一下,您是将新的写入/更新填充表还是旧表? 你什么时候知道你完成了? - Samantha Atkins
@SamanthaAtkins 在迁移正在进行时,由业务逻辑做出的新更改将进入流并被复制。更新迁移标志的目的是为了 1) 至少在最终的每个记录中引起一次更改,以及 2) 允许找到可能尚未遇到任何更改的记录。一旦第5步中的查询不再找到任何记录,所有记录(减去几百毫秒的窗口)都保证存在于新表中。 - Christoph
原始博客文章的链接似乎已经不存在了,我认为相关代码可以在这里找到:https://github.com/abhayachauhan/change-table-schemas - Eliott Paris

3

如果更改涉及更改分区键,您可以添加新的GSI(全局二级索引)。此外,您可以随时向DynamoDB添加新的列/属性,而无需迁移表。


1
同时删除 GSI 将会清除该属性(只要你不犯我所犯的错误,在删除命令中添加 --attribute-definitions 标志)。 - howard

2
我正在使用亚历山大第三种方法的变体。同样,您需要创建一个新表格,该表格将在旧表格更新时进行更新。不同之处在于,在过渡期间,您使用现有服务中的代码同时写入两个表格,而不是使用lambda函数。您可能有不想在临时lambda函数中再次编写的自定义持久性代码,并且很可能您必须为这个新表编写服务代码。根据您的架构,您甚至可以在没有停机时间的情况下切换到新表格。
然而,使用lambda函数的好处是,任何由对新表格的额外写入引入的负载都在lambda上而不是服务上。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接