在软件项目的生命周期中,我们不时需要执行重大更改,这可能会迫使我们修改数据库以适应我们的新行为。
大多数情况下,我们的更改将包括简单的架构迁移,例如添加新的表,列或索引,但有时我们实际上需要迁移数据本身。
出于多种原因,可能需要进行数据迁移。业务逻辑或甚至新技术要求的变化可能导致我们执行数据迁移。
我们应该如何规划数据迁移?重要的是要记住什么?什么是最佳做法?
以下是编写数据迁移的14条规则:
1.不要写数据迁移
我想以这个规则开头会有些讽刺,但这是最重要的规则。
通常,如果我们认为我们需要数据迁移,则意味着我们已经拥有大量数据。
大量数据的迁移存在许多风险,我们甚至没有想到许多边缘情况以及许多方法来搞乱我们的数据,因此我们需要另外的数据迁移来修复我们当前数据迁移中的错误。
与架构迁移不同,数据迁移可能需要花费大量时间。有时,如果您有大量数据,甚至数小时。如果需要,回滚可能比首先运行数据迁移具有更大的风险。
这就是为什么第一条规则是尽可能地避免它。
2.在迁移旧数据之前部署代码更改
有时在编写规则时需要明确说明。
比方说,我们有一个“firstName”和一个“lastName”列,我们需要创建一个“fullName”列。
在迁移旧数据之前,我们应该部署新版本的代码来支持我们的新领域。否则,在运行迁移时,使用旧代码的用户将创建具有空“fullName”字段的新行。
在我们支持新行为(上例中的“fullName”)之后,我们将继续使用永久行数进行迁移。
一旦我们不再创建新的“错误”数据,我们就可以运行数据迁移来完成转换。
这里唯一的例外是,如果我们有一个紧迫的截止日期和大量数据要迁移,我们不希望将最危险的部分留到最后。如果是这样,我们希望两次运行我们的迁移脚本。当我们需要在非常接近截止日期时运行它时,它将快速安全地运行少量数据。
3.不要尝试优化运行时
数据迁移是一次性脚本,风险很大,我们都知道。几秒钟的运行时间不会改变一件事。有许多方法可以减少运行时间。使用线程或多进程并行化工作是一方面加速运行时的一个很好的例子。另一方面,您存在同步问题的风险。
在这样一个冒险的过程中,不要考虑运行时间,考虑如何安全地完成工作,风险最小。
4.估计脚本的运行时间
“知识就是力量”Francis Bacon说,我们都知道他是对的。
尝试估计脚本运行的时间。如果要迭代数据库表和/或在带有数据样本的开发环境中运行它,请计算行数。它会为您提供大量信息,并帮助您做出一些决定。
您可能会发现估计的运行时间太长或太短。
如果时间过长,您可能会考虑选项,例如在专用服务器上运行迁移,将其拆分为几个服务器,每个服务器具有不同的范围或重构某些代码。
如果它非常短,您可以考虑简化脚本或向其添加更多任务。
5.编写幂等代码
数据迁移的主要风险是数据本身。当您要迁移数百万个数据行时,很难预测可以拥有的所有不同情况。
缺少案例可能导致我们的脚本失败并退出。调查和调试故障可能会发现一个新的边缘情况。
那我们该怎么办?
第一个选项是将缺少的案例添加到我们的脚本中。第二个是忽略它。无论如何,我们需要再次重新运行迁移才能完成工作。
编写幂等代码,意味着无论我们运行迁移多少次,结果都是相同的。这是我们应该具备的重要能力。
我们以一个带有“version”列的DB表为例。作为我们迁移的一部分,我们需要将版本增加1。假设我们将分批更新(请继续关注为什么要批量运行)。每个查询将是:
代码语言:javascript复制UPDATE my_table SET version = version 1 WHERE ...
这是非幂等代码的一个示例,在多次执行的情况下可能导致错误的版本值。
我们怎样才能使它成为幂等的?
添加一个列(我们可以在完成后删除)来标记哪些行已完成,可能是一种可能的解决方案。然后我们可以根据需要多次运行迁移。
在每次更新之前,我们将检查它是否已经更新,因此我们不会将版本增加两次。
6.分批运行
运行大量更新或插入查询是一个非常糟糕的主意。如果您为最大查询运行时间设置了阈值(我希望您这样做),它很可能达到该限制并失败。如果没有,它将占用大量内存并且很有可能窒息您的数据库服务器。
我们很多时候都想从其他资源中获取数据。为一个巨大的查询准备所有数据可能是一个错误。
批量运行可以帮助您隔离问题,并让您的服务将其资源用于其他目的,例如提供常规请求。
批量运行时,请注意以下事项:
1.始终在查询中使用order by
。否则您将重复或遗漏记录。
2.不要将查询基于您正在更改的数据。
例如,如果你有两列的表,firstName
和lastName
,而你想创建第三列fullName
。如果您使用SELECT * FROM my_table WHERE fullName IS NOT NULL LIMIT 1000
查询循环数据库会发生什么 ?如果我们的某些迭代因任何原因失败,我们将继续选择它们,这将导致我们进入无限循环。
另一个常见的错误是尝试与我们刚刚更改的过滤数据一起使用OFFSET
和LIMIT
。例如 - SELECT * FROM my_table WHERE fullName IS NOT NULL LIMIT [N] OFFSET [N*Index]
将导致我们跳过记录。由于我们的总计数在每次迭代后都会发生变化,因此我们无法保持OFFSET
价值。
7.对每个资源使用SQL事务
在批量检索数据后,我们还有两个步骤。首先是处理数据。其次是将其保存回我们的数据库。
我们应该如何保存它?
保存时,通常我们不能分批进行。我们需要逐个运行更新查询。
有时,更新资源将需要多个查询。例如,如果我们还需要更新其依赖项。
我们想要一起更新整个资源或失败。为此,我们有SQL事务。
我们希望为每个资源创建一个事务,并将其标记为已完成。
它可能需要更长的时间,但是如果出现故障并重新运行我们的脚本,它将使我们免于错误的行为。
8.蓝绿部署
蓝绿部署是一种无需停机即可部署代码的方法。在运行数据迁移时尝试实现这些原则。
例如,如果我们需要更改列中的数据,我们将创建另一列并运行迁移。完成后,我们将通过重命名来切换列。经过测试和验证,我们可以完全删除旧列。
优点:
- 我们保留了旧数据,因此我们可以轻松回滚
- 我们可以将所有迁移的数据公开在一起,并为用户提供更好的体验
缺点:
- 这是更多的工作,包括在开始迁移之前部署代码来维护两个列
使用这些原则将为您提供运行安全迁移的工具,同时将丢失数据的风险降至最低。
9.写出大量的日志
一旦我们在生产环境中运行,就会发生奇怪的事情。可以显示各种不需要的案例和神秘数据值。
与常规HTTP请求不同,我们可以轻松地一次又一次地尝试,模拟数据迁移中的失败可能是一项非常具有挑战性的任务。
在这些情况下,我们唯一的救世主是我们的日志。
根据需要写入尽可能多的日志。不要担心空间。脚本成功完成后,您可以将其删除。
写入错误,警告,报告进度并计算运行时间。您的日志就是您的眼睛,当您的脚本运行时,您希望将它们保持打开状态。
10.错误 - 跳过或停止?
我们应该考虑像我们在编写的任何代码中那样的意外错误。但在数据迁移中,我们应该考虑另一个方面。
哪些错误会导致我们完全停止我们的脚本?哪些错误是坏的,但我们可以将它们写为对我们日志的警告,稍后修复它们并再次为那些损坏的记录重新运行我们的脚本?
通常,如果错误表明我们的脚本中存在可能导致下一条记录的错误迁移值的错误,我们应该停止我们的脚本。
另一个原因可能是导致所有脚本无法运行的错误。例如,第三方的错误网址 - 一旦发现它就继续运行将是浪费时间。无论如何,所有通话都会失败。
否则,我们可以标记这些行,让我们的脚本继续运行,这样我们至少可以完成它们的运行。
注意错误,不要每次都跳过或停止。
11.第三方调用
如果我们需要从第三方迁移数据,该怎么办?我们该如何处理?
第一个答案是,请不要。它会显着增加运行时间并增加大量意外行为。
避免它的一个好方法是提前获取所有数据。如果您是该第三方的所有者,则可以在数据库中创建所需数据的临时转储。
如果我们考虑所有选项并仍然拨打第三方电话必须参与我们的迁移,该怎么办?
如果是这样,请考虑以下事项:
- 考虑在4XX响应的情况下停止脚本(在429旁边)。它可能指向您的代码实现中的错误。
- 如果您有5XX响应,请继续运行,但请务必记录或标记这些错误。
- 检查第三方服务器是否具有速率限制。如果是这样,请确保不要到达它。
- 考虑为您的呼叫使用重试机制。特别是对于429(请求太多)等错误
12.回滚计划
不管错误什么时候发生,我们都应该做好准备。
回滚的原因可能有所不同,从人为错误到错误的数据修改。
如果我们使用蓝绿策略(规则#7),回滚可以非常简单快捷。如果没有,它可能是地狱,但仍然可行。
无论如何,记住这一点。提前计划并在开发环境中进行测试。确保在作为第二个回滚计划之前备份您的数据。
如果发生灾难,良好的回滚可以挽救您的数据。
13.验证您的迁移
完成后,构建确认脚本以验证您的工作。
如果我们将采取我们的例子中从之前有关合并firstName
和lastName
成fullName
列。一个好的确认策略可以是以下查询:
SELECT COUNT(*) FROM table WHERE CONCAT(firstName, ' ', lastName) != fullName
得到零将表明我们真的完成了。
它可以是脚本的一部分,也可以手动运行。但我们绝对需要提前考虑一项确保我们真正完成的战略。
14.带上你最好的伙伴来完成这项任务
这个不是一个规则,但更像是一个建议。
数据迁移可能是一个不愉快的旅程。它可以运行几个小时,让你在办公室凌晨3点保持清醒。
当脚本运行的时候,你可以和你的伙伴们享受披萨和乒乓球,这样你就不会觉得时间长了:p
就是这些了!我试图将我们在团队中学到的所有知识和经验教训分组,以编写出色的数据迁移。然而,数据迁移可能彼此非常不同。并非所有这些都是必需的,但了解它们可以为您节省大量时间和挫折。
祝你好运,记住,数据迁移是地狱:)
原文标题《14 Rules for writing a data migration》
作者:Guy Segev
译者:February
不代表云加社区观点,更多详情请查看原文链接