为平滑数据库变更而进行的Goose迁移

你好,伙计!

今天,让我们谈谈数据库迁移是什么,以及它们为何如此重要。在当今世界,任何对数据库的更改都应谨慎进行,并按照特定流程进行。理想情况下,这些步骤应该集成到我们的 CI/CD 流水线中,以便一切都可以自动运行。

下面是我们的议程

  1. 问题出在哪里?
  2. 如何解决?
  3. 一个简单的例子
  4. 一个更复杂的例子
  5. 建议
  6. 结果
  7. 结论

问题出在哪里?

如果你的团队从未处理过数据库迁移,也不太确定为什么需要它们,那么让我们来解决这个问题。如果你已经了解基础知识,可以跳过这部分。

主要挑战

当我们对数据库进行“计划内”和“平稳”的更改时,我们需要保持服务可用性,并满足 SLA 要求(以便用户不会遭受停机或延迟)。想象一下,你想要在拥有 500 万用户的表中更改列类型。如果你直接进行这个操作(例如,简单运行 ALTER TABLE 而不进行准备),这个表可能会被锁定很长一段时间 — 这会导致用户无法使用服务。

为了避免这样的麻烦,请遵循两条规则:

  1. 以不锁定表的方式应用迁移(或至少最小化锁定)。
  2. 如果你需要改变列的类型,通常更容易的方法是先创建一个具有正确类型的新列,然后再删除旧列。

另一个问题:版本控制和回滚

有时你需要回滚一个迁移。

手动操作这个过程 — 进入生产数据库并调整数据 — 不仅风险较大,而且如果没有直接访问权限的话,这样做可能根本不可能。这就是专门的迁移工具派上用场的地方。它们让你能够干净地应用更改并在需要时撤销它们。

我们如何解决这个问题?使用正确的工具

每种语言和生态系统都有自己的迁移工具:

  • 对于Java,LiquibaseFlyway很常见。
  • 对于Go,一个受欢迎的选择是goose(我们将在这里看一下)。
  • 等等。

Goose:它是什么以及为什么有用

Goose是一个轻量级的Go实用程序,可以帮助你自动管理迁移。它提供:

  • 简单性。最小的依赖关系和迁移的透明文件结构。
  • 多功能性。支持各种数据库驱动程序(PostgreSQL、MySQL、SQLite等)。
  • 灵活性。可以使用SQL或Go代码编写迁移。

安装Goose

Shell

 

工作原理:迁移结构

默认情况下,Goose会在db/migrations目录中查找迁移文件。每个迁移遵循以下格式:

Shell

 

  • NNN是迁移编号(例如,001002等)。
  • 之后,可以是任何描述性名称,例如init_schema
  • 扩展名可以是.sql.go

SQL迁移示例

文件:001_init_schema.sql:

SQL

 

我们的第一个示例

更改列类型(字符串 → 整数)

假设我们有一个users表,其中有一个类型为VARCHAR(255)age列。现在我们想将其更改为INTEGER。迁移可能如下所示(文件005_change_column_type.sql):

SQL

 

这里发生了什么:

  1. 向上迁移

    • 我们将age列更改为INTEGERUSING (age::INTEGER)子句告诉PostgreSQL如何将现有数据转换为新类型。
    • 请注意,如果age中有任何非数字数据,此迁移将失败。在这种情况下,您将需要更复杂的策略(请参阅下文)。
  2. 向下迁移

    • 如果我们回滚,则将age恢复为VARCHAR(255)
    • 我们再次使用USING (age::TEXT)INTEGER转换回文本。

第二种复杂情况:多步迁移

如果age列可能包含混乱数据(不仅仅是数字),最好分几步来处理:

  1. 添加一个名为age_int的新列,类型为INTEGER
  2. 将有效数据复制到新列中,处理或删除无效条目。
  3. 删除旧列。
SQL

 

为了允许正确的回滚,Down部分只需按相反顺序执行相同的操作。

自动化是关键

为节省时间,将迁移命令添加到Makefile(或任何其他构建系统)中会非常方便。以下是一个带有主要Goose命令的示例Makefile,适用于PostgreSQL

让我们假设:

  • 数据库的DSN为postgres://user:password@localhost:5432/dbname?sslmode=disable
  • 迁移文件位于db/migrations
Shell

 

如何使用?

1. 创建新迁移(SQL文件)。这将生成一个文件db/migrations/002_add_orders_table.sql

Shell

 

2. 应用所有迁移。Goose将在您的数据库中创建一个schema_migrations表(如果尚不存在),并按升序应用任何新迁移。

Shell

 

3. 回滚最后一个迁移。只撤销最后一个。

Shell

 

4. 回滚所有迁移(在生产环境中谨慎使用)。完全重置。

Shell

 

5. 检查迁移状态

Shell

 

输出示例:

Shell

 

总结

通过使用迁移工具和Makefile,我们可以:

  1. 限制直接访问生产数据库,只能通过迁移进行更改。
  2. 轻松跟踪数据库版本并在出现问题时回滚。
  3. 保持单一、一致的数据库更改历史记录。
  4. 执行“平滑”迁移,不会在微服务世界中破坏正在运行的生产环境。
  5. 获得额外验证 — 每个更改都将经历PR和代码审查过程(假设您已经设置了这些设置)。

另一个优点是很容易将所有这些命令集成到您的CI/CD流程中。并记住 — 安全第一。

例如:

YAML

 

结论和建议

主要思想很简单:

  • 保持迁移的规模小而频繁。这样更容易审核、测试,必要时还可以回滚。
  • 在所有环境中使用相同的工具,以确保开发、测试和生产环境保持同步。
  • 将迁移集成到 CI/CD 中,这样就不会依赖于任何人手动运行迁移。

通过这种方式,您将拥有一个可靠且受控的更改数据库结构的流程 — 这样不会影响生产环境,并且可以在出现问题时快速响应。

祝您在迁移中一切顺利!

感谢阅读!

Source:
https://dzone.com/articles/goose-as-crucial-tool-for-your-service