云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

中间件_搭建网站服务器_价格

小七 141 0

大规模在线迁移

工程团队在构建软件时面临一个共同的挑战:他们最终需要重新设计他们用来支持清晰抽象和更复杂特性的数据模型。在生产环境中,这可能意味着迁移数百万个活动对象并重构数千行代码。Stripe用户希望我们的API提供可用性和一致性。这意味着,当我们进行迁移时,我们需要格外小心:存储在系统中的对象需要有准确的值,Stripe的服务需要始终保持可用。在这篇文章中,我们将解释如何安全地对数亿订阅对象进行一次大规模迁移。为什么迁移难?比例尺Stripe拥有数亿订阅对象。对于我们的生产数据库来说,运行一个涉及所有这些对象的大型迁移是一项繁重的工作。假设迁移每个订阅对象需要一秒钟的时间:按照顺序方式,迁移1亿个对象需要3年以上的时间。正常运行时间企业经常以条状交易。我们在线执行所有基础设施升级,而不是依赖计划的维护窗口。因为我们不能在迁移期间简单地暂停订阅服务,所以我们必须在所有服务都以100%的速度运行的情况下执行转换。准确度我们的订阅表在代码库中的许多不同位置使用。如果我们试图在订阅服务中同时更改数千行代码,我们几乎肯定会忽略一些边缘情况。我们需要确保每项服务都能继续依赖于准确的数据。在线迁移的模式将数百万个对象从一个数据库表移动到另一个表很困难,但这是许多公司需要做的事情。有一种常见的4步双写模式,人们经常使用这种模式来进行像这样的大型在线迁移。其工作原理如下:对现有表和新表进行双重写入以保持它们的同步。将代码库中的所有读取路径更改为从新表中读取。将代码库中的所有写入路径更改为只写入新表。删除依赖过时数据模型的旧数据。我们的示例迁移:订阅什么是订阅?为什么需要进行迁移?Stripe Billing帮助像DigitalOcean和Squarespace这样的用户为客户建立和管理重复计费。在过去的几年里,我们不断地添加一些特性来支持他们更复杂的计费模型,比如多个订阅、试用、优惠券和发票。在早期,每个客户对象最多有一个订阅。我们的客户作为个人记录保存。由于客户到订阅的映射很简单,订阅与客户一起存储。类客户订阅订阅结束最终,我们意识到有些用户希望创建具有多个订阅的客户。我们决定将订阅字段(对于单个订阅)转换为订阅字段,以便存储多个活动订阅的数组。类客户数组:订阅订阅结束当我们添加新特性时,这个数据模型就成了问题。对客户订阅的任何更改都意味着更新整个客户记录,以及扫描客户对象的订阅相关查询。所以我们决定单独存储活动订阅。我们重新设计的数据模型将订阅移动到它们自己的表中。作为提醒,我们的四个迁移阶段是:对现有表和新表进行双重写入以保持它们的同步。将代码库中的所有读取路径更改为从新表中读取。将代码库中的所有写入路径更改为只写入新表。删除依赖过时数据模型的旧数据。让我们来回顾一下这四个阶段,就像我们在实践中所看到的那样。第一部分:双重写作我们通过创建一个新的数据库表开始迁移。第一步是开始复制新信息,以便将其写入两个存储区。然后,我们将把丢失的数据回填到新的存储中,以便两个存储区保存相同的信息。所有新的写入操作都应该更新这两个存储。在本例中,我们将所有新创建的订阅记录到Customers表和subscriptions表中。在开始对两个表进行双重写入之前,有必要考虑一下这一额外写入对生产数据库的潜在性能影响。我们可以通过缓慢增加复制对象的百分比来减轻性能问题,同时密切关注操作指标。此时,两个表中都存在新创建的对象,而旧的对象只在旧表中找到。我们将以一种懒散的方式开始复制现有订阅:每当对象被更新时,它们将自动复制到新表中。这种方法允许我们开始以增量方式传输现有订阅。最后,我们将把所有剩余的客户订阅回填到新的订阅表中。我们需要将现有订阅回填到新订阅表中。在实时数据库上填充新表的最昂贵的部分是查找所有需要迁移的对象。通过查询数据库查找所有对象需要对生产数据库进行多次查询,这将花费大量时间。幸运的是,我们能够将其卸载到对生产数据库没有影响的脱机进程中。我们将数据库的快照提供给Hadoop集群,这使得我们可以使用MapReduce以离线、分布式的方式快速处理数据。我们用烫伤来管理我们的MapReduce工作。sperning是一个用Scala编写的有用的库,它使编写MapReduce作业变得更加容易(您可以用10行代码编写一个简单的任务)。在本例中,我们将使用烫伤来帮助我们识别所有订阅。我们将遵循以下步骤:写一个烫手的作业,提供需要复制的所有订阅ID的列表。运行一个大型的多线程迁移,用一组并行高效地操作数据的进程来复制这些订阅。迁移完成后,再次运行热烫作业,以确保订阅表中没有丢失的现有订阅。第2部分:更改所有读取路径现在新旧数据存储已同步,下一步是开始使用新的数据存储来读取所有数据。现在,所有读取都使用现有的Customers表:我们需要移到Subscriptions表。我们需要确保从新订阅表中读取是安全的:我们的订阅数据需要一致。我们将使用GitHub的科学家来帮助我们验证我们的读取路径。Scientist是一个Ruby库,允许您运行实验并比较两个不同代码路径的结果,如果两个表达式在生产中产生不同的结果,就会发出警告。有了科学家,我们可以实时生成不同结果的警报和指标。当实验代码路径生成错误时,应用程序的其余部分不会受到影响。我们将进行以下实验:使用科学家从订阅表和客户表中读取。如果结果不匹配,则提出一个错误,提醒我们的工程师注意不一致性。GitHub的科学家让我们进行实验,从两个表中读取并比较结果。在我们验证了所有内容都匹配之后,我们开始从新表中读取数据。我们的实验是成功的:现在所有的读取都使用新的订阅表。第3部分:更改所有写入路径接下来,我们需要更新写入路径以使用新的订阅存储。我们的目标是逐步推出这些更改,因此我们需要使用谨慎的策略。到目前为止,我们一直在向旧存储区写入数据,然后将其复制到新存储区:现在我们要颠倒顺序:将数据写入新存储,然后将其归档到旧存储中。通过保持这两个存储的一致性,我们可以进行增量更新并仔细观察每个更改。重构修改订阅的所有代码路径无疑是迁移过程中最具挑战性的部分。Stripe处理订阅操作(例如更新、按比例分配、续订)的逻辑跨越多个服务的数千行代码。成功重构的关键是我们的增量过程:我们将尽可能多的代码路径隔离到最小的单元中,这样我们就可以小心地应用每一个更改。我们的两张桌子每一步都要保持一致。对于每一条代码路径,我们需要使用一种整体方法来确保我们的更改是安全的。我们不能用旧记录代替新记录:每一条逻辑都需要仔细考虑。如果我们漏掉了任何一个案例,我们可能会以数据不一致而告终。谢天谢地,我们可以进行更多的科学家实验,以提醒我们任何潜在的不一致。我们新的简化写入路径如下所示:如果调用属性,我们可以通过引发错误来确保没有代码块继续使用过时的订阅数组:类客户def订阅作品:错误.hard("访问客户上的订阅数组")结束结束第4部分:删除旧数据我们最后(也是最令人满意的)步骤是删除写入旧存储的代码,并最终将其删除。一旦确定不再有代码依赖于过时数据模型的subscriptions字段,我们就不再需要写入旧表:有了这个改变,我们的代码不再使用旧的存储,而新的表现在成为我们的真相来源。我们现在可以删除所有Customer对象上的订阅数组,并以一种惰性的方式递增地处理删除操作。我们首先在每次加载订阅时自动清空数组,然后运行最后一个烫手的作业和迁移来查找任何