使用全局表的写入模式
全局表在表级别始终处于主动-主动状态。但是,您可能希望通过控制您路由写入请求的方式来将它们视为主动-被动。例如,您可能决定将写入请求路由到单个区域,以避免潜在的写入冲突。
托管式写入模式主要分为三类:
写入任何区域模式(非主模式)
写入一个区域模式(单主模式)
写入您的区域模式(混合主模式)
您应该考虑哪种写入模式适合您的使用案例。此选择会影响您路由请求、撤离区域和处理灾难恢复的方式。总体最佳做法可能因应用程序的写入模式而异。
写入任何区域模式(非主模式)
写入任何区域模式处于完全主动-主动状态,不会对可能发生写入的位置施加限制。任何区域都可以随时接受写入。这是最简单的模式。此模式只能用于某些类型的应用程序。当所有写入器都是幂等的,因此可以安全地重复时,这很合适,以便跨区域的并发或重复的写入操作不会发生冲突。例如,当用户更新其联系人数据时。此模式也适用于一种特殊的幂等情况,即仅限追加的数据集,其中所有写入操作都是确定性主键下的唯一插入。最后,此模式适用于可以接受写入冲突风险的情况。
![客户端写入任何区域的工作原理图。](images/gt-client-read-write-to-any-region.png)
写入任何区域模式是实现起来最简单的架构。路由更容易,因为任何区域都可以随时成为写入目标。失效转移更容易,因为任何最近的写入操作都可以任意次重播到任何辅助区域。在可能的情况下,您应该针对这种写入模式进行设计。
例如,视频流媒体服务通常使用全局表来跟踪书签、评论、观看状态标志等。这些部署可以使用写入任何区域模式,只要它们确保每次写入都是幂等的,并且项目的下一个正确值不取决于其当前值。对于直接分配用户的新状态的用户更新,例如设置新的最新时间码、分配新的评论或设置新的观看状态,就会出现这种情况。如果将用户的写入请求路由到不同的区域,则最后一次写入操作将持续存在,全局状态将根据最后一次分配而定。此模式下的读取操作在经过最新的 ReplicationLatency
值延迟后,最终将变得一致。
在另一个例子中,一家金融服务公司使用全局表作为系统的一部分,以持续统计每位客户的借记卡购买情况,从而计算该客户的现金返还奖励。新的事务从世界各地流入并转向多个区域。对于目前没有利用全局表的设计,该公司为每个客户使用单个 RunningBalance 项目。客户操作使用 ADD 表达式更新余额,该表达式不是幂等的,因为新的正确值取决于当前值。这意味着,如果在不同的区域中大约在同一时间对同一个余额进行两次写入操作,则余额将不同步。
同一家公司可以通过仔细重新设计 DynamoDB 的全局表来实现写入任何区域模式。新设计可以遵循“事件流式传输”模型,其本质上是带有仅限追加工作流程的账本。每个客户操作都会在为该客户维护的项目集合中追加一个新项目。项目集合是一组共享主键的项目,其排序键不同。附加客户操作的每个写入操作都是幂等插入,使用客户 ID 作为分区键,并使用事务 ID 作为排序键。这种设计使得余额的计算变得更加复杂,因为它需要 Query
先提取项目,然后再进行某种客户端数学运算。但优点是它使所有写入都具有幂等性,从而显著简化了路由和失效转移。有关更多信息,请参阅全局表的请求路由。
再举第三个例子,假设有一位客户在投放在线广告。他们已经决定,为了简化写入任何区域模式的设计,可以接受较低的数据丢失风险。当他们投放广告时,他们只有几毫秒的时间检索足够的元数据来确定要展示的广告,然后记录广告展示,这样同一广告就不会重复展示给该用户了。借助全局表,他们既可以为全世界的终端用户提供低延迟读取,又可以获得低延迟写入。他们可以在单个项目中记录用户的所有广告展示,并将其表示为一个不断增长的列表。他们可以使用一个项目而不是追加到项目集合中,因为这样他们可以在每次写入时删除较早的广告展示,而无需为删除付费。这种写入操作不是幂等的,因此,如果同一个终端用户大概在同一时间看到来自多个区域的广告,则一个广告展示可能会覆盖另一个广告展示。对于在线广告投放,用户偶尔会看到重复广告的风险相对于这种更简单、更有效的设计是值得的。
单主模式(“写入一个区域”)
写入一个区域模式是主动-被动模式,它将所有表写入路由到单个主动区域。请注意,DynamoDB 没有单一主动区域的概念;DynamoDB 外部的应用程序路由对此进行管理。写入一个区域模式通过确保写入一次只流到一个区域来避免写入冲突。当您想使用条件表达式或事务时,这种写入模式很有用,因为除非您知道自己是在针对最新数据采取行动,否则这些条件表达式或事务将不起作用。因此,使用条件表达式和事务会要求将所有写入请求发送到一个包含最新数据的区域。
最终一致性读取可以进入任何副本区域以降低延迟。强一致性读取必须进入单个主区域。
![写入一个区域的工作原理示意图。](images/gt-client-writes-one-region.png)
有时需要更改主动区域来应对区域故障,以帮助处理数据。使用全局表撤离区域是这个使用案例的一个例子。一些客户会定期更改当前主动的区域,例如“全天候式”(follow-the-sun)部署。这会将主动区域置于活动最多的地理位置附近,从而使其读取和写入延迟最低。它还有一个附带好处,那就是每天调用区域不断变化的代码路径,确保在进行任何灾难恢复之前都经过良好的测试。
被动区域可能会在 DynamoDB 周围保留一组缩小规模的基础设施,而只有当被动区域成为主动区域时,此类基础设施才会建立起来。有关指示灯和热备用设计的更深入讨论,请参阅 AWS 上的灾难恢复(DR)架构,第 III 部分:指示灯和热备用
在利用全局表进行低延迟的全局分布式读取时,使用写入一个区域模式效果很好。例如,一家大型社交媒体公司拥有数百万用户和数十亿个帖子。每个用户在创建账户时都会被分配到一个区域,地理位置位于其所在位置附近。他们的所有数据都放到该非全局表中。该公司使用一个单独的全局表来保存用户到其主区域的映射,使用的是写入一个区域模式。该公司在世界各地保留只读副本,以帮助直接找到每个用户的数据,同时最大限度地减少增加的延迟。更新很罕见(仅在将用户的主区域从一个区域移到另一个区域时),并且始终要经过一个区域进行写入,以避免出现写入冲突的可能性。
再举一个例子,考虑一位实施了每日现金返还计算的金融服务客户。客户使用写入任何区域模式来计算余额,但使用写入一个区域模式来跟踪实际的现金返还付款。如果他们想为客户一天每消费 10 美元奖励 1 便士,他们需要 Query
前一天的所有交易,计算花费总额,将现金返还决定写入新表,删除查询的项目集以将其标记为已消费,并将它们替换为一个单一项目,其中存储应该用来进行第二天计算的任何剩余金额。这项工作需要交易信息,因此在写入一个区域模式下效果会更好。只要工作负载不可能发生重叠,应用程序就可以混合使用写入模式,即使在同一个表上也是如此。
混合主模式(“写入您的区域”)
写入您的区域模式将不同的数据子集分配给不同的区域,并且仅允许通过其主区域对项目进行写入操作。此模式是主动-被动模式,但会根据项目分配主动区域。每个区域都是其自己的非重叠数据集的主区域,必须保护写入操作以确保位置正确。
此模式与写入一个区域类似,不同之处在于它支持较低的写入延迟,因为与每个终端用户关联的数据可以放在离该用户更近的网络上。此模式还可以在各区域之间更均匀地分布周围的基础设施,并且在失效转移方案中构建基础设施所需的工作量更少,因为所有区域的基础设施都将有一部分已经处于活动状态。
![有关客户端如何写入到单个区域中的每个项目的工作原理图。](images/get-client-writes-each-item-single-region.png)
可以通过多种方式确定项目的主区域:
固有:数据的某些方面可以清楚地表明数据所在的主区域,例如数据的分区键。例如,客户和有关该客户的所有数据将在客户数据中被标记为以某特定区域为主区域。使用区域固定来为 Amazon DynamoDB 全局表中的项目设置主区域
中介绍了此技术 已协商:通过某种外部方式协商每个数据集的主区域,例如与维护分配的单独全局服务进行协商。分配可能有一个有限的期限,在此之后需要重新协商。
面向表:不是单个复制全局表,而是拥有与复制区域一样多的全局表。每个表的名称都指示其主区域。在标准操作中,所有数据都写入主区域,而其他区域则保留只读副本。在失效转移期间,另一个区域将临时承担对该表的写入职责。
例如,假设您在一家游戏公司工作。您需要为全世界的所有游戏玩家提供低延迟的读取和写入。您可以将每位游戏玩家的主区域设为离他们最近的区域。该区域会承担他们所有的读取和写入操作,从而确保始终具有很强的写入后读取一致性。但是,如果该游戏玩家外出旅行或其主区域发生中断,则其数据的完整副本将在备用区域中可用。因此,可以将游戏玩家分配到不同的主区域,这很有用。
再举一个例子,假设您在一家视频会议公司工作。每次电话会议的元数据都会分配到一个特定的区域。呼叫者可以使用离他们最近的区域以实现最低延迟。如果出现区域中断,使用全局表可以快速恢复,因为系统可以将呼叫处理转移到已经有数据复制副本的其他区域。