Help us learn about your current experience with the documentation. Take the survey.

避免迁移中的停机时间

在处理数据库时,某些操作可能需要停机时间。由于迁移过程中不能出现停机,我们需要采用一系列步骤来实现相同的结果而不影响服务。本指南描述了那些看似需要停机的操作、其影响,以及如何在不要求停机的情况下执行它们。

删除列

删除列操作较为棘手,因为正在运行的 GitLab 进程期望这些列存在。即使列未被引用,ActiveRecord 在启动时也会缓存表结构。

当列未被明确标记为忽略时,就会发生这种情况。

此外,任何引用此类列的数据库视图也需要一并考虑。

要安全地解决此问题,需要在三个版本中分三步进行:

  1. 忽略该列(版本 M)
  2. 删除该列(版本 M+1)
  3. 移除忽略规则(版本 M+2)

我们将此过程分散到三个版本中,是因为删除列是破坏性操作,无法轻松回滚。

遵循此流程可确保没有部署到 GitLab.com 的操作,且 GitLab 自托管实例的升级过程不会合并任何这些步骤。

忽略该列(版本 M)

第一步是在应用程序代码中忽略该列,并移除所有对它的代码引用(包括模型验证)。
此步骤是必要的,因为 Rails 会缓存列信息并在多处重用此缓存。
可通过定义要忽略的列来实现。例如,在版本 12.5 中,要忽略 User 模型的 updated_at 列,需使用以下代码:

class User < ApplicationRecord
  ignore_column :updated_at, remove_with: '12.7', remove_after: '2019-12-22'
end

也可忽略多个列:

ignore_columns %i[updated_at created_at], remove_with: '12.7', remove_after: '2019-12-22'

如果模型存在于 CE 和 EE 中,则必须在 CE 模型中忽略该列。如果模型仅存在于 EE 中,则需在 EE 中添加。

我们需要指定何时可以安全移除列忽略规则:

  • remove_with:设置为 GitLab 版本,通常在添加列忽略后的两个版本(M+2)(示例中为 12.7)。
  • remove_after:设置为日期,在此日期后我们认为可以安全移除列忽略规则,通常在 M+1 版本发布后、M+2 开发周期内。例如,12.7 的开发周期为 2019-12-182020-01-17,而 12.6删除列 的版本,因此可将日期安全设置为 2019-12-22(即 12.6 的发布日期)。

此信息帮助我们更好地推理列忽略规则,并确保我们不会过早移除列忽略规则(无论是常规版本还是部署到 GitLab.com)。例如,这避免了部署包含忽略列和后续移除列忽略规则(会导致停机)的批量更改的情况。

在此示例中,忽略列的更改已进入版本 12.5

忽略和删除列不应在同一个版本中同时进行。在模型中正确忽略列之前删除列,可能导致零停机迁移问题:运行中的实例在查找已删除列时可能失败,直到 Rails 架构缓存过期。对于尝试遵循零停机升级的自托管客户,这会迫使他们显式重启所有运行的 GitLab 实例以重新加载更新的架构。为避免此情况,请先忽略列(版本 M),然后在下一个版本中删除它(版本 M+1)。

忽略被数据库视图引用的列

当列被数据库视图引用时(如下例):

CREATE VIEW recently_updated_users_view(id, username, updated_at) AS
SELECT id, username, updated_at
FROM users
WHERE updated_at > now() - interval '30 day'

ignore_columns 指令也应包含在相应的模型类中:

class RecentlyUpdatedUsersView < ApplicationRecord
  self.table_name = 'recently_updated_users_view'

  ignore_columns :updated_at
end

删除该列(版本 M+1)

继续我们的示例,删除列的操作将进入版本 12.6部署后迁移

首先创建 部署后迁移

bundle exec rails g post_deployment_migration remove_users_updated_at_column

编写删除列的迁移时,必须考虑以下场景:

被删除的列没有属于它的索引或约束

在这种情况下,可使用 事务性迁移

class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.1]
  def up
    remove_column :users, :updated_at
  end

  def down
    add_column :users, :updated_at, :datetime
  end
end

被删除的列有属于它的索引或约束

如果 down 方法需要重新添加任何被删除的索引或约束,则不能在事务性迁移中完成。迁移代码如下:

class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    remove_column :users, :updated_at
  end

  def down
    add_column(:users, :updated_at, :datetime, if_not_exists: true)

    # 确保重新添加在 `up` 方法中删除的任何索引或约束。例如:
    add_concurrent_index(:users, :updated_at)
  end
end

down 方法中,我们检查列是否已存在再重新添加。
这样做是因为迁移是非事务性的,可能在运行过程中失败。

disable_ddl_transaction! 用于禁用包装整个迁移的事务。

有关数据库迁移的更多信息,请参阅 迁移风格指南

被删除的列被数据库视图引用

当列被数据库视图引用时,其行为类似于附加了约束,因此需要先更新视图再删除列:

  1. 重新创建视图(排除该列)
  2. 从原始表中删除列

down 方法应按相反顺序执行操作,因为列在被视图引用前必须存在:

  1. 将列重新添加到原始表
  2. 重新创建视图(重新包含该列)

迁移代码如下:

class RemoveUsersUpdatedAtColumn < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    execute <<-SQL
      DROP VIEW IF EXISTS recently_updated_users_view;

      CREATE VIEW recently_updated_users_view(id, username) AS
        SELECT id, username
        FROM users;
    SQL

    remove_column :users, :updated_at
  end

  def down
    add_column :users, :updated_at, :datetime

    execute <<-SQL
      DROP VIEW IF EXISTS recently_updated_users_view;

      CREATE VIEW recently_updated_users_view(id, username, updated_at) AS
        SELECT id, username, updated_at
        FROM users
        WHERE updated_at > now() - interval '30 day'
    SQL
  end
end

移除忽略规则(版本 M+2)

在下一个版本(本例中为 12.7),我们创建另一个合并请求来移除忽略规则。
这将移除 ignore_column 行,并在不再需要时移除 IgnoreableColumns 的包含。

此操作只能在 remove_with 指定的版本合并,且 remove_after 日期已过后执行。

重命名列

以下流程仅适用于小表。该流程在常规迁移中复制所有数据,对于大表可能耗时过长。对于大表,应考虑使用 批量后台迁移 在多个里程碑中复制数据并执行重命名。

标准方式重命名列需要停机时间,因为应用程序可能在数据库迁移期间或之后继续使用旧列名。
要无停机地重命名列,我们需要两个迁移:常规迁移和部署后迁移。这两个迁移可以放在同一个版本中。
步骤如下:

  1. 添加常规迁移(版本 M)
  2. 忽略该列(版本 M)
  3. 添加部署后迁移(版本 M)
  4. 移除忽略规则(版本 M+1)

当列被数据库视图引用时,按标准方式重命名列无需额外步骤,因为视图会更新为新列名,同时保持 SELECT 部分不变。

无停机迁移时,上述步骤中有额外注意事项。

添加常规迁移(版本 M)

首先需要创建常规迁移。此迁移应使用 Gitlab::Database::MigrationHelpers#rename_column_concurrently 执行重命名。例如:

# 常规迁移位于 db/migrate
class RenameUsersUpdatedAtToUpdatedAtTimestamp < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    rename_column_concurrently :users, :updated_at, :updated_at_timestamp
  end

  def down
    undo_rename_column_concurrently :users, :updated_at, :updated_at_timestamp
  end
end

这会处理列重命名,确保数据同步,并复制索引和外键。

如果列包含一个或多个不包含原始列名的索引,则上述流程会失败。此时需要重命名这些索引。

当列被数据库视图引用时,需要重新创建视图并指向新列。down 操作需要在执行 undo_rename_column_concurrently 之前恢复视图:

# 常规迁移位于 db/migrate,包含数据库视图重建
class RenameUsersUpdatedAtToUpdatedAtTimestamp < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    rename_column_concurrently :users, :updated_at, :updated_at_timestamp

    execute <<-SQL
      DROP VIEW IF EXISTS recently_updated_users_view;

      CREATE VIEW recently_updated_users_view(id, username, updated_at) AS
        SELECT id, username, updated_at_timestamp
        FROM users
        WHERE updated_at > now() - interval '30 day'
    SQL
  end

  def down
    execute <<-SQL
      DROP VIEW IF EXISTS recently_updated_users_view;

      CREATE VIEW recently_updated_users_view(id, username, updated_at) AS
        SELECT id, username, updated_at
        FROM users
        WHERE updated_at > now() - interval '30 day'
    SQL

    undo_rename_column_concurrently :users, :updated_at, :updated_at_timestamp
  end
end

忽略该列(版本 M)

下一步是在应用程序代码中忽略该列,并确保它未被使用。
此步骤是必要的,因为 Rails 会缓存列信息并在多处重用此缓存。
此步骤与 删除列时的第一步 类似,适用条件相同。

class User < ApplicationRecord
  ignore_column :updated_at, remove_with: '12.7', remove_after: '2019-12-22'
end

添加部署后迁移(版本 M)

重命名过程需要在部署后迁移中进行清理。
我们可以使用 Gitlab::Database::MigrationHelpers#cleanup_concurrent_column_rename 执行此清理:

# 部署后迁移位于 db/post_migrate
class CleanupUsersUpdatedAtRename < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    cleanup_concurrent_column_rename :users, :updated_at, :updated_at_timestamp
  end

  def down
    undo_cleanup_concurrent_column_rename :users, :updated_at, :updated_at_timestamp
  end
end

如果重命名的是 大表,请仔细考虑第一个迁移已运行但第二个清理迁移尚未运行时的状态。
通过 Canary 系统,系统可能在此状态下运行相当长的时间。

移除忽略规则(版本 M+1)

与删除列时相同,重命名完成后,我们需要在后续版本中 移除忽略规则

更改列约束

添加或删除 NOT NULL 子句(或其他约束)通常无需停机时间。
添加 NOT NULL 约束需要应用程序更改部署,因此应在部署后迁移中进行。
相反,删除 NOT NULL 约束应在常规迁移中进行,这样任何插入 NULL 值的代码都可以安全运行。

避免使用 change_column,因为它会重新定义整个列类型,效率低下。

有关每种特定用例的指南,请查看以下内容:

更改列类型

可以使用 Gitlab::Database::MigrationHelpers#change_column_type_concurrency 更改列类型。
此方法与 rename_column_concurrency 类似。例如,如果我们要将 users.username 的类型从 string 更改为 text

  1. 创建常规迁移
  2. 创建部署后迁移
  3. 将数据转换为新类型

当更改被数据库视图引用的列类型时,视图需要作为过程的一部分重新创建。

创建常规迁移

常规迁移用于创建一个具有临时名称的新列,并设置一些触发器以保持数据同步。
此类迁移如下所示:

# 常规迁移位于 db/migrate
class ChangeUsersUsernameStringToText < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    change_column_type_concurrently :users, :username, :text
  end

  def down
    undo_change_column_type_concurrently :users, :username
  end
end

当列被数据库视图引用时,需要重新创建视图并指向新的临时列。
在后续步骤中,当临时列重命名为原始名称时,视图会自动更新,无需其他更改:

# 常规迁移位于 db/migrate,包含数据库视图重建
class ChangeUsersUsernameStringToText < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    change_column_type_concurrently :users, :username, :text

    # 临时列名遵循此模式:`"#{column}_for_type_change"`
    # 因此名为 `username` 的列变为 `username_for_type_change`

    execute <<-SQL
      DROP VIEW IF EXISTS recently_updated_users_view;

      CREATE VIEW recently_updated_users_view(id, username, updated_at) AS
        SELECT id, username_for_type_change, updated_at
        FROM users
        WHERE updated_at > now() - interval '30 day'
    SQL
  end

  def down
    execute <<-SQL
      DROP VIEW IF EXISTS recently_updated_users_view;

      CREATE VIEW recently_updated_users_view(id, username, updated_at) AS
        SELECT id, username, updated_at_timestamp
        FROM users
        WHERE updated_at > now() - interval '30 day'
    SQL

    undo_change_column_type_concurrently :users, :username
  end
end

创建部署后迁移

接下来我们需要使用部署后迁移清理更改:

# 部署后迁移位于 db/post_migrate
class ChangeUsersUsernameStringToTextCleanup < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  def up
    cleanup_concurrent_column_type_change :users, :username
  end

  def down
    undo_cleanup_concurrent_column_type_change :users, :username, :string
  end
end

至此,操作完成!

将数据转换为新类型

某些类型更改需要将数据转换为新类型。例如从 text 转换为 jsonb
此时使用 type_cast_function 选项。
确保没有坏数据且转换总能成功。也可以提供自定义函数处理转换错误。

示例迁移:

  def up
    change_column_type_concurrently :users, :settings, :jsonb, type_cast_function: 'jsonb'
  end

更改列默认值

更改列默认值较为困难,因为 Rails 处理等于默认值的值的方式存在问题。

如果启用了 partial_inserts 配置,Rails 在插入记录时不会向 PostgreSQL 发送默认值,而是将此任务留给数据库。当迁移更改列的默认值时,由于架构缓存,运行中的应用程序对此更改一无所知。这会导致应用程序意外写入错误数据到数据库的风险,尤其是在运行数据库迁移很长时间后才部署新代码版本时。

如果运行中的代码显式写入列的旧默认值,必须遵循多步流程,防止 Rails 在显式指定旧默认值的 INSERT 查询中用新默认值替换旧默认值。

这需要在两个次版本中执行步骤:

  1. SafelyChangeColumnDefault 关联添加到模型 并在部署后迁移中更改默认值。
  2. 在下一个次版本中清理 SafelyChangeColumnDefault 关联

我们必须等待一个次版本再清理 SafelyChangeColumnDefault,因为自托管版本会将整个次版本打包为单次零停机部署。

SafelyChangeColumnDefault 关联添加到模型并在部署后迁移中更改默认值

第一步是在应用程序代码中将该列标记为安全更改。

class Ci::Build < ApplicationRecord
  include SafelyChangeColumnDefault

  columns_changing_default :partition_id
end

然后创建 部署后迁移 来更改默认值:

bundle exec rails g post_deployment_migration change_ci_builds_default
class ChangeCiBuildsDefault < Gitlab::Database::Migration[2.1]
  def change
    change_column_default('ci_builds', 'partition_id', from: 100, to: 101)
  end
end

在下一个次版本中清理 SafelyChangeColumnDefault 关联

在下一个次版本中,创建新的合并请求移除 columns_changing_default 调用。如果不再需要,也移除 SafelyChangeColumnDefault 的包含(除非用于其他列)。

更改大表的架构

虽然 change_column_type_concurrentlyrename_column_concurrently 可用于无停机更改表架构,但它们对大表效果不佳。因为所有工作按顺序执行,迁移可能耗时过长,阻止部署进行。它们还可能因数据库快速顺序更新多行而产生巨大压力。

为减少数据库压力,应在迁移大表(例如 issues)的列时使用后台迁移。后台迁移将工作/负载分散到更长时间内,不会减慢部署速度。

更多信息,请参阅 清理批量后台迁移的文档

添加索引

使用 add_concurrent_index 添加索引无需停机时间。

更多信息,请参阅 迁移风格指南

删除索引

删除索引无需停机时间。

添加表

此操作是安全的,因为尚无代码使用该表。

删除表

可以使用部署后迁移安全地删除表,但前提是应用程序不再使用该表。

按照 数据库字典 中描述的过程,将表添加到 db/docs/deleted_tables。即使表被删除,它仍被数据库迁移引用。

重命名表

重命名表需要停机时间,因为应用程序可能在数据库迁移期间或之后继续使用旧表名。

如果表和 ActiveRecord 模型尚未使用,删除旧表并创建新表是“重命名”表的首选方法。

通过遵循我们的多版本 重命名表流程,可以无停机地重命名表。

添加外键

添加外键可能导致停机,请参阅 FK: 避免停机和迁移失败 文档了解详情。

integer 主键迁移到 bigint

为避免某些具有 integer 主键(PK)的表 存在溢出风险,我们必须将其 PK 迁移到 bigint。以下描述了无停机且避免对数据库造成过大负载的流程。

初始化转换并开始迁移现有数据(版本 N)

首先添加常规迁移以创建新的 bigint 列。使用提供的 initialize_conversion_of_integer_to_bigint 辅助方法。该方法还会创建数据库触发器以保持两列对新记录的同步(代码):

# frozen_string_literal: true

class InitializeConversionOfMergeRequestMetricsToBigint < Gitlab::Database::Migration[2.1]
  disable_ddl_transaction!

  TABLE = :merge_request_metrics
  COLUMNS = %i[id]

  def up
    initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS)
  end

  def down
    revert_initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS)
  end
end

忽略新的 bigint 列:

# frozen_string_literal: true

class MergeRequest::Metrics < ApplicationRecord
  ignore_column :id_convert_to_bigint, remove_with: '16.0', remove_after: '2023-05-22'
end

入队批量后台迁移(代码)以迁移现有数据:

# frozen_string_literal: true

class BackfillMergeRequestMetricsForBigintConversion < Gitlab::Database::Migration[2.1]
  restrict_gitlab_migration gitlab_schema: :gitlab_main

  TABLE = :merge_request_metrics
  COLUMNS = %i[id]

  def up
    backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS, sub_batch_size: 200)
  end

  def down
    revert_backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS)
  end
end
  • 通过 Issue#438124,新实例的所有 ID 列均为 bigint。旧实例(包括 Gitlab.com SaaS)中尚未转换为 bigint 的 ID 列列表维护在 db/integer_ids_not_yet_initialized_to_bigint.yml 中。请勿手动编辑此文件 - 它会在 清理过程 中自动更新。
  • 由于架构文件中的所有 ID 已为 bigint,请勿对 db/structure.sql 进行任何更改。

监控后台迁移

在迁移运行时检查其执行情况。以下是多种监控方法。

批量后台迁移的高级状态

请参阅 如何检查批量后台迁移的状态

查询数据库

我们可以直接查询相关数据库表。需要只读副本访问权限。
示例查询:

-- 获取给定表的批量后台迁移详情
SELECT * FROM batched_background_migrations WHERE table_name = 'namespaces'\gx

-- 按状态统计给定表的批量后台迁移作业数量
SELECT
  batched_background_migrations.id, batched_background_migration_jobs.status, COUNT(*)
FROM
  batched_background_migrations
  JOIN batched_background_migration_jobs ON batched_background_migrations.id = batched_background_migration_jobs.batched_background_migration_id
WHERE
  table_name = 'namespaces'
GROUP BY
  batched_background_migrations.id, batched_background_migration_jobs.status;

-- 给定表的批量后台迁移进度(基于估计的总元组数)
SELECT
  m.table_name,
  LEAST(100 * sum(j.batch_size) / pg_class.reltuples, 100) AS percentage_complete
FROM
  batched_background_migrations m
  JOIN batched_background_migration_jobs j ON j.batched_background_migration_id = m.id
  JOIN pg_class ON pg_class.relname = m.table_name
WHERE
  j.status = 3 AND m.table_name = 'namespaces'
GROUP BY m.id, pg_class.reltuples;

Sidekiq 日志

我们还可以使用 Sidekiq 日志监控执行批量后台迁移的 Worker:

  1. 使用 @gitlab.com 邮箱登录 Kibana
  2. 将索引模式更改为 pubsub-sidekiq-inf-gprd*
  3. 添加过滤器 json.queue: cronjob:database_batched_background_migration

PostgreSQL 慢查询日志

慢查询日志记录了执行时间超过 1 秒的查询。要查看批量后台迁移的慢查询:

  1. 使用 @gitlab.com 邮箱登录 Kibana
  2. 将索引模式更改为 pubsub-postgres-inf-gprd*
  3. 添加过滤器 json.endpoint_id.keyword: Database::BatchedBackgroundMigrationWorker
  4. 可选。要仅查看更新,添加过滤器 json.command_tag.keyword: UPDATE
  5. 可选。要仅查看失败语句,添加过滤器 json.error_severity.keyword: ERROR
  6. 可选。按表名添加过滤器。

Grafana 仪表盘

要监控数据库健康状况,请使用以下额外指标:

  • PostgreSQL 元组统计:如果看到正在转换的表更新率高,或此表的死元组百分比增加,可能意味着 autovacuum 无法跟上。
  • PostgreSQL 概览:如果看到主数据库服务器上系统使用率高或每秒事务数(TPS)高,可能意味着迁移导致问题。

Prometheus 指标

每个批量后台迁移的 指标 会发布到 Prometheus。这些指标可在 Grafana 中搜索和可视化(示例)。

交换列(版本 N + 1)

后台迁移完成后,且新 bigint 列已为所有记录填充,我们可以交换列。交换通过部署后迁移完成。具体过程取决于正在转换的表,但通常按以下步骤:

  1. 使用提供的 ensure_backfill_conversion_of_integer_to_bigint_is_finished 辅助方法,确保批量迁移已完成。
    如果迁移未完成,后续步骤仍会失败。提前检查旨在提供更有用的错误信息。

    disable_ddl_transaction!
    
    restrict_gitlab_migration gitlab_schema: :gitlab_ci
    
    def up
      ensure_backfill_conversion_of_integer_to_bigint_is_finished(
        :ci_builds,
        %i[
          project_id
          runner_id
          user_id
        ],
        # 可选。仅当无主键时需要,例如 schema_migrations。
        primary_key: :id
      )
    end
    
    def down; end
  2. 使用 Gitlab::Database::MigrationHelpers::ConvertToBigint 模块中的 add_bigint_column_indexes 辅助方法,创建与使用 integer 列的现有索引匹配的 bigint 列索引。

    • 辅助方法预期会创建所有必需的 bigint 索引,但建议重新检查以确保我们没有遗漏任何现有索引。有关辅助方法的更多信息,请参阅合并请求 135781
  3. 使用 bigint 列创建外键(FK),使其与使用 integer 列的现有 FK 匹配。这既包括引用其他表的 FK,也包括引用正在迁移表的 FK(示例)。

  4. 在事务中交换列:

    1. 锁定涉及的表。为减少死锁风险,建议按父到子顺序执行(示例)。
    2. 重命名列以交换名称(示例)。
    3. 重置触发器函数(示例)。
    4. 交换默认值(示例)。
    5. 交换 PK 约束(如果存在)(示例)。
    6. 删除旧索引并重命名新索引(示例)。
      • 使用 add_bigint_column_indexes 辅助方法创建的 bigint 索引名称可通过调用 Gitlab::Database::MigrationHelpers::ConvertToBigint 模块中的 bigint_index_name 获取。
    7. 删除旧外键(如果仍存在)并重命名新外键(示例)。

示例合并请求 merge request迁移

删除触发器和旧 integer 列(版本 N + 2)

使用部署后迁移和提供的 cleanup_conversion_of_integer_to_bigint 辅助方法,删除数据库触发器和旧的 integer 列(示例)。

移除忽略规则(版本 N + 3)

在列被删除后的下一个版本中,移除忽略规则,因为我们不再需要它们(示例)。

数据库视图

GitLab 对数据库视图的使用较少,因为它们在处理迁移时可能引入额外复杂性。

目前有两种使用视图的情况:

Postgres 内部指标

Postgres 内部指标可通过 Gitlab::Database::Postgres* 模型(位于 lib/gitlab/database)访问,并依赖 Gitlab::Database::SharedModel 类。

统一备份 CLI

统一备份 CLI 依赖几个视图来检索触发 gitaly-backup 所需的有限信息(针对多种仓库类型)。视图可通过 Gitlab::Backup::Cli::Models::*(位于 gems/gitlab-backup-cli/lib/gitlab/backup/cli/models)访问,并依赖 Gitlab::Backup::Cli::Models::Base 类处理连接。

由于统一备份 CLI 代码位于单独的 gem 中,主代码库还包含规范以确保所需视图返回工具所需的信息。这确保了两个代码库之间的“契约”。

如果视图所需的任何列需要更改,请遵循以下步骤:

  • 删除列
    • 与 Durability 团队(负责统一备份)和 Gitaly(负责 gitaly-backup)协调
  • 重命名列
  • 更改列类型

数据迁移

数据迁移可能很棘手。迁移数据的常用方法是采用 3 步流程:

  1. 迁移初始批次数据
  2. 部署应用程序代码
  3. 迁移任何剩余数据

这通常有效,但并非总是如此。例如,如果要将字段的格式从 JSON 更改为其他格式,我们就会遇到问题。如果在部署应用程序代码之前更改现有数据,我们很可能会遇到错误。另一方面,如果在部署应用程序代码后迁移,我们可能会遇到同样的问题。

如果仅需纠正一些无效数据,通常部署后迁移就足够了。如果需要更改数据格式(例如从 JSON 改为其他格式),通常最好添加一个新列用于新数据格式,并让应用程序使用它。在这种情况下,流程如下:

  1. 添加新格式的新列
  2. 将现有数据复制到新列
  3. 部署应用程序代码
  4. 在部署后迁移中,复制任何剩余数据

通常没有一刀切的解决方案,因此最好在合并请求中讨论此类迁移,以确保以最佳方式实施它们。