如何通过 Golang 使用迁移
简单的示例应用程序展示如何使用 golang-migrate
为什么应该使用迁移?
很多人问这个问题,我试图列出这个列表来强调使用迁移的主要优点:
版本控制:主要也是最重要的之一是能够对数据库模式的不同修改进行版本控制。如果没有迁移,这些架构更改将是不连贯的并且无法跟踪,这将导致版本控制问题和可能的错误。
回滚:总是需要有一个回滚系统,以防出现任何故障。迁移系统始终有两种方法,向上应用数据库中的更改,向下负责快速一致地恢复更改:-)
自动化和 CI/CD 集成: 迁移可以自动化,使其成为 CI/CD 管道的一部分。这有助于顺利、一致地部署更改,无需手动干预。
我们可以找到更多优点,但我认为这些点很好地总结了主要优点。
如何在Golang中实现迁移?
Go 本身不支持该功能的迁移,因此我们可以使用流行的 golang-migrate 包,如果您使用像 GORM 这样的 ORM,您也可以使用它。
这两个包都很受欢迎,但在这个例子中我将使用 golang-migrate,因为我对实现 ORM 不感兴趣。
显示代码!
让我们一步一步来实现一个简单的应用程序,看看它是如何使用的。
要阅读本文,您需要:Go 和 Docker 以及 Docker Compose
基础设施
在根目录中创建文件 docker-compose.yml,我们将在其中定义您最喜欢的数据库,在我的例子中使用 MariaDB,但也可以随意使用另一个数据库。
services: mariadb: image: mariadb:11.5.2 container_name: mariadb_example_go_migration ports: - "3306:3306" environment: - MYSQL_DATABASE=app - MYSQL_ROOT_PASSWORD=root - TZ=Europe/Berlin volumes: - mariadbdata:/var/lib/mysql volumes: mariadbdata: driver: local docker compose up -d
如果您愿意,可以直接使用 Docker 而不是 docker-compose:
docker volume create -d local mariadbdata docker run --name mariadb_example_go_migration -p 3306:3306 -e MYSQL_DATABASE=app -e MYSQL_ROOT_PASSWORD=root -e TZ=Europe/Berlin -v mariadbdata:/var/lib/mysql mariadb:11.5.2
环境价值观
在根目录中创建或更新文件 .env,您需要在其中定义变量以连接我们的数据库。
DATABASE_DSN=root:root@tcp(localhost:3306)/app
创建一个简单的 golang 应用程序
创建一个简单的golang应用程序以确保成功的数据库连接并列出数据库中的所有表和结构及其结构。 cmd/main.go
package main import ( "database/sql" "fmt" "log" "os" "text/tabwriter" _ "github.com/go-sql-driver/mysql" "github.com/joho/godotenv" ) func main() { // Load .env variables err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // Open connection with MySQL DB db, err := sql.Open("mysql", os.Getenv("DATABASE_DSN")) if err != nil { log.Fatalf("Error opening database: %v\n", err) } defer db.Close() // Ensure that the connection works err = db.Ping() if err != nil { log.Fatalf("Error connecting database: %v\n", err) } fmt.Println("Connected to database") // Execute the SHOW TABLES query to list all tables in the database tables, err := db.Query("SHOW TABLES") if err != nil { log.Fatalf("Failed to execute SHOW TABLES query: %v\n", err) } defer tables.Close() fmt.Println("Database structure:") for tables.Next() { var tableName string if err := tables.Scan(&tableName); err != nil { log.Fatalf("Failed to scan table name: %v\n", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', tabwriter.Debug) fmt.Printf("\n[Table: %s]\n\n", tableName) fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", "Field", "Type", "Null", "Key", "Default", "Extra") // Get the structure of the current table structureQuery := fmt.Sprintf("DESCRIBE %s", tableName) columns, err := db.Query(structureQuery) if err != nil { log.Fatalf("Failed to describe table %s: %v\n", tableName, err) } defer columns.Close() for columns.Next() { var field, colType, null, key, defaultVal, extra sql.NullString err := columns.Scan(&field, &colType, &null, &key, &defaultVal, &extra) if err != nil { log.Fatalf("Failed to scan column: %v\n", err) } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", field.String, colType.String, null.String, key.String, defaultVal.String, extra.String) } w.Flush() } }
当我们运行它时,我们有类似的输出:
迁移 CLI
要运行 golang-migrate CLI 基本上有两种方法在本地安装 CLI 或通过官方 Docker 镜像运行:migrate/migrate。
就我个人而言,我更喜欢 de docker 变体,但在本教程中说明了这两种变体。
如何生成迁移
第一步是使用下一个命令创建一个空迁移。
services: mariadb: image: mariadb:11.5.2 container_name: mariadb_example_go_migration ports: - "3306:3306" environment: - MYSQL_DATABASE=app - MYSQL_ROOT_PASSWORD=root - TZ=Europe/Berlin volumes: - mariadbdata:/var/lib/mysql volumes: mariadbdata: driver: local docker compose up -d
docker volume create -d local mariadbdata docker run --name mariadb_example_go_migration -p 3306:3306 -e MYSQL_DATABASE=app -e MYSQL_ROOT_PASSWORD=root -e TZ=Europe/Berlin -v mariadbdata:/var/lib/mysql mariadb:11.5.2
- ext:要生成的文件的扩展名。
- dir:将在其中创建迁移的目录。
- seq:迁移序列名称。
此命令将在database/migrations/文件夹中生成两个空文件:000001createuserstable.up.sql和000001createuserstable.down.sql
在 000001createuserstable.up.sql 文件中定义创建表 users 的 SQL:
DATABASE_DSN=root:root@tcp(localhost:3306)/app
在 000001createuserstable.down.sql 文件中定义 SQL 来恢复 up 所做的所有更改,在这种情况下我们必须删除 users 表:
package main import ( "database/sql" "fmt" "log" "os" "text/tabwriter" _ "github.com/go-sql-driver/mysql" "github.com/joho/godotenv" ) func main() { // Load .env variables err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // Open connection with MySQL DB db, err := sql.Open("mysql", os.Getenv("DATABASE_DSN")) if err != nil { log.Fatalf("Error opening database: %v\n", err) } defer db.Close() // Ensure that the connection works err = db.Ping() if err != nil { log.Fatalf("Error connecting database: %v\n", err) } fmt.Println("Connected to database") // Execute the SHOW TABLES query to list all tables in the database tables, err := db.Query("SHOW TABLES") if err != nil { log.Fatalf("Failed to execute SHOW TABLES query: %v\n", err) } defer tables.Close() fmt.Println("Database structure:") for tables.Next() { var tableName string if err := tables.Scan(&tableName); err != nil { log.Fatalf("Failed to scan table name: %v\n", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', tabwriter.Debug) fmt.Printf("\n[Table: %s]\n\n", tableName) fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", "Field", "Type", "Null", "Key", "Default", "Extra") // Get the structure of the current table structureQuery := fmt.Sprintf("DESCRIBE %s", tableName) columns, err := db.Query(structureQuery) if err != nil { log.Fatalf("Failed to describe table %s: %v\n", tableName, err) } defer columns.Close() for columns.Next() { var field, colType, null, key, defaultVal, extra sql.NullString err := columns.Scan(&field, &colType, &null, &key, &defaultVal, &extra) if err != nil { log.Fatalf("Failed to scan column: %v\n", err) } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", field.String, colType.String, null.String, key.String, defaultVal.String, extra.String) } w.Flush() } }
如何申请移民
以下命令应用所有待处理的迁移。您还可以通过在 up 后添加数字来定义要应用的迁移数量。
#CLI variant migrate create -ext sql -dir ./database/migrations -seq create_users_table
#Docker CLI variant docker run --rm -v $(pwd)/database/migrations:/migrations migrate/migrate \ create -ext sql -dir /migrations -seq create_users_table
- 路径:迁移目录的路径。
- 数据库:定义数据库 DSN 连接。
注意:第一次运行迁移时,将创建一个表“schema_migrations”,其中迁移知道所应用的版本号。
并运行我们的 Golang 应用程序来显示结果:
添加新迁移
在用户表上添加新的电话列
CREATE TABLE `users` ( `id` VARCHAR(36) NOT NULL PRIMARY KEY, `name` VARCHAR(255) NOT NULL, `email` VARCHAR(255) NOT NULL UNIQUE, `password` VARCHAR(255) NOT NULL );
DROP TABLE IF EXISTS `users`;
#CLI variant migrate -path=./database/migrations -database "mysql://root:root@tcp(localhost:3306)/app" up
#Docker CLI variant docker run --rm -v $(pwd)/database/migrations:/migrations --network host migrate/migrate \ -path=/migrations -database "mysql://root:root@tcp(localhost:3306)/app" up
当您从我们的 Golang 应用程序运行它时,您可以看到新字段:
如何恢复迁移
通过以下命令我们可以轻松回滚已应用的。迁徙。在下面的示例中,我们可以看到如何反转上次应用的迁移:
#CLI variant migrate create -ext sql -dir ./database/migrations -seq add_column_phone #Docker CLI variant docker run --rm -v $(pwd)/database/migrations:/migrations migrate/migrate \ create -ext sql -dir /migrations -seq add_column_phone
-- 000002_add_column_phone.up.sql ALTER TABLE `users` ADD `phone` VARCHAR(255) NULL;
警告:如果您没有定义迁移数量,ROLLBACK将应用于所有迁移!
然后我们可以显示上次迁移已恢复并且电话字段已被删除:-)
如何解决迁移错误
如果迁移包含错误并被执行,则无法应用该迁移,并且迁移系统将阻止数据库上的任何进一步迁移,直到此迁移得到修复。
当尝试申请时,我们会收到如下消息:
services: mariadb: image: mariadb:11.5.2 container_name: mariadb_example_go_migration ports: - "3306:3306" environment: - MYSQL_DATABASE=app - MYSQL_ROOT_PASSWORD=root - TZ=Europe/Berlin volumes: - mariadbdata:/var/lib/mysql volumes: mariadbdata: driver: local docker compose up -d
不要惊慌,恢复一致的系统并不难。
首先,我们必须解决损坏的迁移问题,在本例中为版本 2。
迁移解决后,我们必须强制系统使用最新的有效版本,在本例中为版本 1。
docker volume create -d local mariadbdata docker run --name mariadb_example_go_migration -p 3306:3306 -e MYSQL_DATABASE=app -e MYSQL_ROOT_PASSWORD=root -e TZ=Europe/Berlin -v mariadbdata:/var/lib/mysql mariadb:11.5.2
DATABASE_DSN=root:root@tcp(localhost:3306)/app
现在您可以毫无问题地重新应用迁移;-)
生成文件
为了提高我们的工作效率并方便使用这些命令,我们可以使用 Makefile。下面您可以看到两个变体:本机客户端和 docker。
CLI 变体
package main import ( "database/sql" "fmt" "log" "os" "text/tabwriter" _ "github.com/go-sql-driver/mysql" "github.com/joho/godotenv" ) func main() { // Load .env variables err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // Open connection with MySQL DB db, err := sql.Open("mysql", os.Getenv("DATABASE_DSN")) if err != nil { log.Fatalf("Error opening database: %v\n", err) } defer db.Close() // Ensure that the connection works err = db.Ping() if err != nil { log.Fatalf("Error connecting database: %v\n", err) } fmt.Println("Connected to database") // Execute the SHOW TABLES query to list all tables in the database tables, err := db.Query("SHOW TABLES") if err != nil { log.Fatalf("Failed to execute SHOW TABLES query: %v\n", err) } defer tables.Close() fmt.Println("Database structure:") for tables.Next() { var tableName string if err := tables.Scan(&tableName); err != nil { log.Fatalf("Failed to scan table name: %v\n", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', tabwriter.Debug) fmt.Printf("\n[Table: %s]\n\n", tableName) fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", "Field", "Type", "Null", "Key", "Default", "Extra") // Get the structure of the current table structureQuery := fmt.Sprintf("DESCRIBE %s", tableName) columns, err := db.Query(structureQuery) if err != nil { log.Fatalf("Failed to describe table %s: %v\n", tableName, err) } defer columns.Close() for columns.Next() { var field, colType, null, key, defaultVal, extra sql.NullString err := columns.Scan(&field, &colType, &null, &key, &defaultVal, &extra) if err != nil { log.Fatalf("Failed to scan column: %v\n", err) } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", field.String, colType.String, null.String, key.String, defaultVal.String, extra.String) } w.Flush() } }
Docker CLI 变体
#CLI variant migrate create -ext sql -dir ./database/migrations -seq create_users_table
存储库
本教程的代码可以在公共场合找到:GitHub - albertcolom/example-go-migration
原文发表于:albertcolom.com
以上是如何通过 Golang 使用迁移的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Go语言在构建高效且可扩展的系统中表现出色,其优势包括:1.高性能:编译成机器码,运行速度快;2.并发编程:通过goroutines和channels简化多任务处理;3.简洁性:语法简洁,降低学习和维护成本;4.跨平台:支持跨平台编译,方便部署。

Golang在并发性上优于C ,而C 在原始速度上优于Golang。1)Golang通过goroutine和channel实现高效并发,适合处理大量并发任务。2)C 通过编译器优化和标准库,提供接近硬件的高性能,适合需要极致优化的应用。

GoimpactsdevelopmentPositationalityThroughSpeed,效率和模拟性。1)速度:gocompilesquicklyandrunseff,ifealforlargeprojects.2)效率:效率:ITScomprehenSevestAndArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdEcceSteral Depentencies,增强开发的简单性:3)SimpleflovelmentIcties:3)简单性。

Golang和Python各有优势:Golang适合高性能和并发编程,Python适用于数据科学和Web开发。 Golang以其并发模型和高效性能着称,Python则以简洁语法和丰富库生态系统着称。

Golang在性能和可扩展性方面优于Python。1)Golang的编译型特性和高效并发模型使其在高并发场景下表现出色。2)Python作为解释型语言,执行速度较慢,但通过工具如Cython可优化性能。

Golang和C 在性能竞赛中的表现各有优势:1)Golang适合高并发和快速开发,2)C 提供更高性能和细粒度控制。选择应基于项目需求和团队技术栈。

C 更适合需要直接控制硬件资源和高性能优化的场景,而Golang更适合需要快速开发和高并发处理的场景。1.C 的优势在于其接近硬件的特性和高度的优化能力,适合游戏开发等高性能需求。2.Golang的优势在于其简洁的语法和天然的并发支持,适合高并发服务开发。

Golang和C 在性能上的差异主要体现在内存管理、编译优化和运行时效率等方面。1)Golang的垃圾回收机制方便但可能影响性能,2)C 的手动内存管理和编译器优化在递归计算中表现更为高效。
