
Overall
0
Activity
Funds Held
Trading Pairs
0
Registered Location
-
Followers
0
从关键生产系统上的大型跨数据库数据迁移中学到的经验教训
作者:资深软件工程师Alex Ghise
在2019年,Coinbase着手加强其产品构建的基础设施,并为支持当前和未来的产品线奠定坚实的基础。作为这项工作的一部分,我们决定采用AWS RDS PostgreSQL作为关系工作负载的首选数据库,并采用AWS DynamoDB作为键值存储。
我们决定重点关注的第一个领域是如何跟踪余额和转移资金。我们的产品各自设计了自己的解决方案,并且某些遗留系统也受到技术债务和性能问题的困扰。快速准确地处理交易的能力是Coinbase建立面向世界的开放式金融系统的使命的核心。
我们设计并构建了一种新的分类帐服务,以快速,准确地满足产品中所有当前和将来的需求,并进行了迄今为止最大的迁移,移走了超过10亿行公司和客户交易,并平衡了先前数据存储中的信息到我们基于PostgreSQL的新解决方案中,无需进行定期维护,也不会对用户产生明显影响。
我们的主要学习成果:
- 使其具有可重复性-您可能第一次没有正确使用它。
- 快速进行-这样您就可以快速进行迭代。
- 使过程变得平稳—通过设计流程使其运行而不会中断正常的业务运营。
这是我们的工作方式…
迁移要求
准确性和正确性:由于我们在与资金打交道,因此我们知道这将是一个非常敏感的迁移,并希望采取一切预防措施,确保将所有最后的麻烦都考虑在内。
可重复性:此外,新系统与旧系统的数据形状完全不同。此外,我们不得不处理整体应用程序中随着时间而积累的技术债务和杂项。我们知道我们需要考虑一次无法正确解决所有问题的可能性,因此我们想设计一个可重复的过程,我们可以反复进行此过程,直到正确为止。
无需维护停机时间:我们希望Coinbase上的每笔交易都能在执行的同时执行。我们不想进行任何计划的维护或为转换花费任何停机时间。
设置
我们可以将迁移分解为两个独立的问题:切换实时写入和读取新服务,以及将所有历史数据迁移到新服务中。
对于迁移,我们决定采用双重写入/双重读取阶段方法。第1阶段是迁移之前的阶段,在该阶段中,我们只有旧系统。在阶段2中,我们介绍了新系统。我们将从两者读取的读取路径双重写入旧系统和新系统,然后记录差异并从旧系统返回结果。在第3阶段中,我们已经建立了对新设置的信心,因此在返回结果时会偏爱它。我们仍然有旧系统,可以根据需要切换回旧系统。最后,我们逐步淘汰未使用的代码以完成迁移(第4阶段)。
我们不会详细介绍双重写入的实现,因为一般的想法以前已经在行业博客中讨论过。
有趣的是,在第2阶段和第3阶段之间发生了一些事情,即将所有客户数据回填到我们的新系统中,以便我们可以实现奇偶校验。
可重复的回填过程
我们考虑了多种方法来回填十亿多行,这些行代表了从一开始就在Coinbase上进行的所有交易,各有利弊。
最直接的解决方案是在应用程序级别上完成所有操作,利用我们为双重写入而设置的分类帐客户端实现。它的优点是可以使用与实时写入相同的代码路径-从旧到新的单个映射就可以维护。但是,我们将不得不修改服务接口以允许回填,并且如果发生故障,我们将不得不建立长时间运行的进程以及检查点机制。我们还对该解决方案进行了基准测试,发现它太慢了,无法满足我们对快速迭代的要求。
我们最终决定采用基于ETL的解决方案。从高层次上讲,这需要从ETL版本的源数据库中生成回填数据,并将其转储到S3中,然后直接将其加载到目标Postgres数据库中。这种方法的一个主要优点是使用SQL进行数据转换既快速又容易。我们可以在大约20分钟内运行整个数据生成步骤,检查输出,验证内部一致性,并直接在输出上进行数据质量检查,而不必运行整个回填管道。
我们的数据平台提供商提供了用于程序访问的各种连接器和驱动程序。这意味着我们可以使用标准的软件开发工具和生命周期-我们为数据转换编写的代码已经过测试,审查和检入存储库。
它还具有一流的支持,可将数据卸载到S3中,这使我们在配置适当的资源后很容易导出数据。
另一端,AWS提供了aws_s3 postgres扩展,该扩展允许从S3存储桶将数据批量导入数据库。但是,直接将其导入实时生产表存在问题,因为将数亿行插入索引表很慢,而且还影响了实时写入的延迟。
我们通过创建活动表的未索引副本来解决此问题,如下所示:
现在,导入受到I / O的限制,这成为了瓶颈。通过将数据拆分为多个文件,并在顺序导入之间添加了较短的睡眠间隔,我们最终降低了速度。
接下来,在表上重新创建索引。幸运的是,Postgres通过使用CONCURRENT关键字,可以在不写锁定表的情况下创建索引。这允许表在创建索引时继续进行写操作。
到现在为止还挺好。然而,真正的复杂性来自我们对迁移的要求,该迁移不涉及计划的维护或停止事务处理。我们希望目标数据库能够保持实时写入而不会丢失单个写入,并且希望回填的数据无缝连接到实时数据。每个事务都存储有关所涉及的所有帐户的累计余额的信息,这使情况变得更加复杂-这使我们可以轻松地评估和维护数据完整性,并在任何时间戳下查找任何帐户的时间点余额。
为此,我们使用触发器将活动表的插入,更新,删除复制到回填表中,从而解决了这一问题。通过并发索引生成,我们可以在创建索引时写入这些表。
索引完成后,在单个事务中,我们翻转了回填表和活动表,删除了触发器,并删除了现在不需要的表。实时写操作继续进行,好像什么也没发生。
在此过程的最后,我们运行另一个脚本,该脚本遍历所有数据并通过重新创建累积余额和顺序事务之间的链接来恢复数据完整性。
最后但并非最不重要的一点是,我们针对旧数据存储库进行了另一轮完整性检查和比较,以确保数据正确且完整。
放在一起,序列如下所示:
- 清理板岩:重置分类帐数据库,开始双向写入
- 等待将双重写入数据加载到ETL中,以便实时写入数据和回填数据之间存在重叠。
- 生成回填数据,并将其卸载到S3中
- 创建回填表,设置触发器以将数据复制到回填表中。
- 从S3导入数据
- 创建索引
- 翻转表,删除触发器,删除旧表。
- 运行修复脚本
- 验证数据完整性,正确性,完整性
该过程需要4到6个小时才能运行,并且大部分是自动化的。我们在解决数据质量问题和修复错误时一遍又一遍地做。
善后
我们最后的迁移和回填并不是一件难忘的事情。我们没有“作战室”,没有待命的待命工程师,只是我们流程的另一次运行,之后我们决定是时候进行切换了。公司中的大多数人都不高兴。平稳的一天。
我们使用分类帐服务已经快一年了。与以前的系统相比,我们有能力维持每秒更多数量级的事务,并且在延迟方面有严格的限制。现有功能和新功能(例如Coinbase卡)都依靠分类帐服务来实现快速,准确的余额和交易。
Coinbase内部分类账的无缝迁移的艺术最初发布在Medium上的The Coinbase Blog上,人们通过突出并响应这个故事来继续进行对话。