使用 Golang 迁移
什么是迁移?
迁移被广泛用于维护数据库结构的版本控制,它是保持数据库井然有序的一个非常有用的解决方案。
假设您有一个用户表,并且需要在该表中插入一个新字段,如果没有迁移,您将需要手动运行 SQL,例如:
ALTER TABLE "users" ADD COLUMN "phone" VARCHAR(255) NULL;
现在,每次您需要重新创建users
表时,您都需要记住创建此字段,除非您更改users
表的原始创建,但随着表和应用程序的增长,这变得不可行,因此使用迁移是一个很好的选择。
迁徙
迁移的工作方式相对简单,我们通常有一个up
文件和另一个down
文件,一些 ORM(如PrismaORM)只创建 1 个文件,在该up
文件中我们创建将创建或更改数据库的 SQL,在该down
文件中我们创建撤消更改的 SQL。
有什么好处?
现在有了这些文件,我们维护了数据库更改的历史记录,每个更改都有其up
文件down
,现在如果我们需要创建表,我们运行所有up
文件,一切都会创建,如果需要恢复,只需运行down
。
Go 中的迁移
Go 本身不支持使用迁移,但我们可以使用具有此功能的 ORM,例如社区最常用的 GORM,但我们可以在不使用 ORM 的情况下使用迁移,为此我们将使用golang -migrate包。
Golang 迁移包
golang-migrate包是最适合此用途的,我们已经拥有管理迁移所需的一切,并且它几乎支持所有数据库,因此在我们的示例中我们将使用 PostgreSQL。
示例项目
我之前创建了一个简单的项目,但我会快速解释它,因为重点是使用迁移。
我们将有这个结构,非常简单,示例的代码应该在main.go
:
package main
import (
"database/sql"
"fmt"
"log"
"os"
"github.com/joho/godotenv"
_ "github.com/lib/pq"
)
func main() {
// load .env file
godotenv.Load()
postgresURI := os.Getenv("DATABASE_URL")
db, err := sql.Open("postgres", postgresURI)
if err != nil {
log.Panic(err)
}
err = db.Ping()
if err != nil {
db.Close()
log.Panic(err)
}
fmt.Println("Connected to database")
// keep the program running
select {}
}
使用 golang-migrate
我们需要安装 golang-migrate 包的 CLI,查看如何安装这里,运行命令:
migrate -version
如果输出类似于:
v4.16.2
好的,继续!下一步是使用以下命令创建我们的第一个迁移:
migrate create -ext=sql -dir=internal/database/migrations -seq init
ext
:定义扩展,我们使用sql。dir
:这是我们将创建迁移的目录。seq
:定义迁移文件名的序列,我们将使用数字,也可以使用时间戳。
这样,您会注意到在数据库文件夹内创建了一个名为migrations的文件夹。
文件up
和down
文件按顺序创建,因为第一个是000001,如果再次运行该migrate create
命令,它将创建迁移000002。现在让我们创建 SQL 并运行迁移:
在该up
文件中,我们将创建下表:
CREATE TABLE users (
id VARCHAR(36) NOT NULL PRIMARY KEY,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP(3) NOT NULL
);
现在down
我们将在文件中删除表格:
DROP TABLE IF EXISTS users;
准备好 SQL 后,我们就可以运行up
迁移了,不要忘记确保数据库正在运行,为此我在项目中留下了一个 docker compose 文件来运行 PostgreSQL 镜像。
migrate -path=internal/database/migrations -database "postgresql://golang_migrate:golang_migrate@localhost:5432/golang_migrate?sslmode=disable" -verbose up
path
:定义我们的迁移位置。database
:数据库连接url。-verbose
:仅显示所有运行。
如果我们访问PgAdmin或Beekeeper之类的客户端,或者通过 bash 访问其容器并通过 CLI 检查,我们可以看到该表已成功创建:
现在我们可以运行了down
,它是完全相同的命令,但是从 更改up
为down
:
migrate -path=internal/database/migrations -database "postgresql://golang_migrate:golang_migrate@localhost:5432/golang_migrate?sslmode=disable" -verbose down
这样,表格就被删除了。
添加更多字段
现在让我们看看如果需要向表中添加另一个字段会是什么样子users
,如果没有迁移,我们就必须直接更改原始表,但是有了迁移,我们就不需要了,让我们创建另一个迁移:
migrate create -ext=sql -dir=internal/database/migrations -seq init
它将创建顺序为000002up
的迁移,让我们添加字段:down
phone
up
:
ALTER TABLE "users" ADD COLUMN "phone" VARCHAR(255) NULL;
down
:
ALTER TABLE "users" DROP COLUMN "phone";
再次运行时:
migrate -path=internal/database/migrations -database "postgresql://golang_migrate:golang_migrate@localhost:5432/golang_migrate?sslmode=disable" -verbose up
我们的phone
字段已添加到用户表中,但是如果我只想down
在添加该字段的迁移中添加它,该怎么办phone
?可以,只需使用相同的命令,传递值 1,这意味着您要撤消上一次迁移:
migrate -path=internal/database/migrations -database "postgresql://golang_migrate:golang_migrate@localhost:5432/golang_migrate?sslmode=disable" -verbose down 1
该phone
字段已被删除。
让 CLI 更易于使用
如您所见,golang-migrate 命令使用起来可能有点累,我们可以通过使用文件makefile使其更容易。
include .env
create_migration:
migrate create -ext=sql -dir=internal/database/migrations -seq init
migrate_up:
migrate -path=internal/database/migrations -database "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=disable" -verbose up
migrate_down:
migrate -path=internal/database/migrations -database "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=disable" -verbose down
.PHONY: create_migration migrate_up migrate_down
我们创建运行migrate
命令的快捷方式,我们需要使用包含我们的环境include .env
,然后我们创建命令:
create_migration
:创建我们的迁移文件。migrate_up
:运行迁移up
。migrate_down
:运行迁移down
。PHONY
:它将执行一个命令,makefile 可能会尝试获取一个文件,migrate_up
例如,如果存在具有该名称的文件。
有了它,只需使用以下命令:
make create_migration
我们将创建迁移文件,这适用于创建的其他快捷方式。
最后的考虑
在本文中,我们了解了如何使用迁移,以及它对于维护变更历史记录和方便数据库维护的重要性。但值得一提的是,错误地使用迁移可能会导致数据库中的数据丢失,因此了解迁移的工作原理以及 ORM 或编程语言如何处理迁移非常重要。
避免删除迁移,如果使用不当,它们可能会成为一场噩梦。
存储库链接
存储库项目
请参阅我博客上的这篇文章
鏂囩珷鏉ユ簮锛�https://dev.to/wiliamvj/using-migrations-with-golang-3449