Sind Sie schon einmal mit der Situation konfrontiert, dass Sie ein neues Update in der Produktion mit aktualisierten Datenbankschemata bereitstellen, danach aber Fehler auftreten und Dinge rückgängig gemacht werden müssen? Dann kommt die Migration ins Spiel.
Die Datenbankmigration dient mehreren wichtigen Zwecken:
Um ein umfassendes, produktionstaugliches Setup für einen Golang-Dienst mithilfe von GORM mit MySQL zu erstellen, das einfache Migrationen, Aktualisierungen und Rollbacks ermöglicht, müssen Sie Migrationstools einbinden, das Datenbankverbindungspooling verwalten und die richtigen Strukturdefinitionen sicherstellen. Hier ist ein vollständiges Beispiel, das Sie durch den Prozess führt:
/golang-service |-- main.go |-- database | |-- migration.go |-- models | |-- user.go |-- config | |-- config.go |-- migrations | |-- ... |-- go.mod
package config import ( "fmt" "log" "os" "time" "github.com/joho/godotenv" "gorm.io/driver/mysql" "gorm.io/gorm" ) var DB *gorm.DB func ConnectDB() { err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // charset=utf8mb4: Sets the character set to utf8mb4, which supports all Unicode characters, including emojis. // parseTime=True: Tells the driver to automatically parse DATE and DATETIME values into Go's time.Time type. // loc=Local: Uses the local timezone of the server for time-related queries and storage. dsn := fmt.Sprintf( "%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", os.Getenv("DB_USER"), os.Getenv("DB_PASS"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_NAME"), ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("failed to connect database") } sqlDB, err := db.DB() if err != nil { panic("failed to configure database connection") } // Set connection pool settings sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) sqlDB.SetConnMaxLifetime(time.Hour) // 1.sqlDB.SetMaxIdleConns(10) // Sets the maximum number of idle (unused but open) connections in the connection pool. // A value of 10 means up to 10 connections can remain idle, ready to be reused. // 2. sqlDB.SetMaxOpenConns(100): // Sets the maximum number of open (active or idle) connections that can be created to the database. // A value of 100 limits the total number of connections, helping to prevent overloading the database. // 3. sqlDB.SetConnMaxLifetime(time.Hour): // Sets the maximum amount of time a connection can be reused before it’s closed. // A value of time.Hour means that each connection will be kept for up to 1 hour, after which it will be discarded and a new connection will be created if needed. DB = db }
package database import ( "golang-service/models" "golang-service/migrations" "gorm.io/gorm" ) func Migrate(db *gorm.DB) { db.AutoMigrate(&models.User{}) // Apply additional custom migrations if needed }
package models import "gorm.io/gorm" type User struct { gorm.Model Name string `json:"name"` }
DB_USER=root DB_PASS=yourpassword DB_HOST=127.0.0.1 DB_PORT=3306 DB_NAME=yourdb
package main import ( "golang-service/config" "golang-service/database" "golang-service/models" "github.com/gin-gonic/gin" "gorm.io/gorm" ) func main() { config.ConnectDB() database.Migrate(config.DB) r := gin.Default() r.POST("/users", createUser) r.GET("/users/:id", getUser) r.Run(":8080") } func createUser(c *gin.Context) { var user models.User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } if err := config.DB.Create(&user).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(201, user) } func getUser(c *gin.Context) { id := c.Param("id") var user models.User if err := config.DB.First(&user, id).Error; err != nil { c.JSON(404, gin.H{"error": "User not found"}) return } c.JSON(200, user) }
Für Produktionsumgebungen könnten wir eine Migrationsbibliothek wie golang-migrate verwenden, um Migrationen anzuwenden, rückgängig zu machen oder zu wiederholen.
Installieren Sie golang-migrate:
go install -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
Migrationsdateien für Benutzertabelle generieren
migrate create -ext=sql -dir=./migrations -seq create_users_table
Nachdem wir den Befehl ausgeführt haben, erhalten wir ein Paar .up.sql (zur Aktualisierung des Schemas) und down.sql (für ein mögliches späteres Rollback). Die Zahl 000001 ist der automatisch generierte Migrationsindex.
/golang-service |-- migrations | |-- 000001_create_users_table.down.sql | |-- 000001_create_users_table.up.sql
Fügen Sie den relevanten SQL-Befehl zur .up-Datei und .down-Datei hinzu.
000001_create_users_table.up.sql
CREATE TABLE users ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, created_at DATETIME, updated_at DATETIME, deleted_at DATETIME);
000001_create_users_table.down.sql
DROP TABLE IF EXISTS users;
Führen Sie die Hochmigration aus und übernehmen Sie Änderungen an der Datenbank mit dem folgenden Befehl (Flag -verbose, um weitere Protokolldetails anzuzeigen):
migrate -path ./migrations -database "mysql://user:password@tcp(localhost:3306)/dbname" -verbose up
Falls wir Probleme mit der Migration haben, können wir den folgenden Befehl verwenden, um die aktuelle Migrationsversion und ihren Status anzuzeigen:
migrate -path ./migrations -database "mysql://user:password@tcp(localhost:3306)/dbname" version
Wenn wir aus irgendeinem Grund eine fehlerhafte Migration haben, können wir in Betracht ziehen, den Force-Befehl (vorsichtig verwenden) mit der Versionsnummer der schmutzigen Migration zu verwenden. Wenn die Version 1 ist (könnte in der Tabelle migrations oder schema_migrations überprüft werden), würden wir Folgendes ausführen:
migrate -path ./migrations -database "mysql://user:password@tcp(localhost:3306)/dbname" force 1
Irgendwann möchten wir möglicherweise neue Funktionen hinzufügen, und einige davon erfordern möglicherweise eine Änderung der Datenschemata. Beispielsweise möchten wir der Benutzertabelle ein E-Mail-Feld hinzufügen. Wir würden es wie folgt machen.
Führen Sie eine neue Migration durch, um die E-Mail-Spalte zur Benutzertabelle hinzuzufügen
migrate create -ext=sql -dir=./migrations -seq add_email_to_users
Jetzt haben wir ein neues Paar von .up.sql und .down.sql
/golang-service |-- migrations | |-- 000001_create_users_table.down.sql | |-- 000001_create_users_table.up.sql | |-- 000002_add_email_to_users.down.sql | |-- 000002_add_email_to_users.up.sql
Folgenden Inhalt zu *_add_email_to_users.*.sql-Dateien
hinzufügen000002_add_email_to_users.up.sql
/golang-service |-- main.go |-- database | |-- migration.go |-- models | |-- user.go |-- config | |-- config.go |-- migrations | |-- ... |-- go.mod
000002_add_email_to_users.down.sql
package config import ( "fmt" "log" "os" "time" "github.com/joho/godotenv" "gorm.io/driver/mysql" "gorm.io/gorm" ) var DB *gorm.DB func ConnectDB() { err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // charset=utf8mb4: Sets the character set to utf8mb4, which supports all Unicode characters, including emojis. // parseTime=True: Tells the driver to automatically parse DATE and DATETIME values into Go's time.Time type. // loc=Local: Uses the local timezone of the server for time-related queries and storage. dsn := fmt.Sprintf( "%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", os.Getenv("DB_USER"), os.Getenv("DB_PASS"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_NAME"), ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("failed to connect database") } sqlDB, err := db.DB() if err != nil { panic("failed to configure database connection") } // Set connection pool settings sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) sqlDB.SetConnMaxLifetime(time.Hour) // 1.sqlDB.SetMaxIdleConns(10) // Sets the maximum number of idle (unused but open) connections in the connection pool. // A value of 10 means up to 10 connections can remain idle, ready to be reused. // 2. sqlDB.SetMaxOpenConns(100): // Sets the maximum number of open (active or idle) connections that can be created to the database. // A value of 100 limits the total number of connections, helping to prevent overloading the database. // 3. sqlDB.SetConnMaxLifetime(time.Hour): // Sets the maximum amount of time a connection can be reused before it’s closed. // A value of time.Hour means that each connection will be kept for up to 1 hour, after which it will be discarded and a new connection will be created if needed. DB = db }
Führen Sie den Befehl „up migration“ erneut aus, um die Datenschemata zu aktualisieren
package database import ( "golang-service/models" "golang-service/migrations" "gorm.io/gorm" ) func Migrate(db *gorm.DB) { db.AutoMigrate(&models.User{}) // Apply additional custom migrations if needed }
Wir müssen auch die Golang-Benutzerstruktur aktualisieren (das Feld E-Mail hinzufügen), um sie mit den neuen Schemata synchron zu halten.
package models import "gorm.io/gorm" type User struct { gorm.Model Name string `json:"name"` }
Falls wir aus irgendeinem Grund Fehler mit neuen aktualisierten Schemata haben und ein Rollback durchführen müssen, verwenden wir in diesem Fall den Down-Befehl:
DB_USER=root DB_PASS=yourpassword DB_HOST=127.0.0.1 DB_PORT=3306 DB_NAME=yourdb
Nummer 1 gibt an, dass wir eine Migration rückgängig machen möchten.
Hier müssen wir auch die Golang-Benutzerstruktur manuell aktualisieren (entfernen Sie das Feld E-Mail), um die Änderungen am Datenschema widerzuspiegeln.
package main import ( "golang-service/config" "golang-service/database" "golang-service/models" "github.com/gin-gonic/gin" "gorm.io/gorm" ) func main() { config.ConnectDB() database.Migrate(config.DB) r := gin.Default() r.POST("/users", createUser) r.GET("/users/:id", getUser) r.Run(":8080") } func createUser(c *gin.Context) { var user models.User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } if err := config.DB.Create(&user).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(201, user) } func getUser(c *gin.Context) { id := c.Param("id") var user models.User if err := config.DB.First(&user, id).Error; err != nil { c.JSON(404, gin.H{"error": "User not found"}) return } c.JSON(200, user) }
Um den Prozess der Migration und des Rollbacks zu vereinfachen, können wir ein Makefile hinzufügen.
go install -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
Der Inhalt von Makefile wie folgt.
migrate create -ext=sql -dir=./migrations -seq create_users_table
Jetzt können wir einfach make migrate_up oder make migrate_down auf der CLI ausführen, um die Migration und das Rollback durchzuführen.
Bevor Sie eine Migration rückgängig machen oder Änderungen vornehmen, die sich möglicherweise auf Ihre Datenbank auswirken könnten, sollten Sie die folgenden wichtigen Punkte berücksichtigen.
Daher ist es wichtig, Ihre Daten zu sichern. Hier ist eine kurze Anleitung:
Datenbank-Dump:
Verwenden Sie datenbankspezifische Tools, um eine vollständige Sicherung Ihrer Datenbank zu erstellen. Für MySQL können Sie Folgendes verwenden:
/golang-service |-- main.go |-- database | |-- migration.go |-- models | |-- user.go |-- config | |-- config.go |-- migrations | |-- ... |-- go.mod
Dadurch wird eine Datei (backup_before_rollback.sql) erstellt, die alle Daten und das Schema der dbname-Datenbank enthält.
Spezifische Tabellen exportieren:
Wenn Sie nur bestimmte Tabellen sichern müssen, geben Sie diese im mysqldump-Befehl an:
package config import ( "fmt" "log" "os" "time" "github.com/joho/godotenv" "gorm.io/driver/mysql" "gorm.io/gorm" ) var DB *gorm.DB func ConnectDB() { err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // charset=utf8mb4: Sets the character set to utf8mb4, which supports all Unicode characters, including emojis. // parseTime=True: Tells the driver to automatically parse DATE and DATETIME values into Go's time.Time type. // loc=Local: Uses the local timezone of the server for time-related queries and storage. dsn := fmt.Sprintf( "%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", os.Getenv("DB_USER"), os.Getenv("DB_PASS"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_NAME"), ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("failed to connect database") } sqlDB, err := db.DB() if err != nil { panic("failed to configure database connection") } // Set connection pool settings sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) sqlDB.SetConnMaxLifetime(time.Hour) // 1.sqlDB.SetMaxIdleConns(10) // Sets the maximum number of idle (unused but open) connections in the connection pool. // A value of 10 means up to 10 connections can remain idle, ready to be reused. // 2. sqlDB.SetMaxOpenConns(100): // Sets the maximum number of open (active or idle) connections that can be created to the database. // A value of 100 limits the total number of connections, helping to prevent overloading the database. // 3. sqlDB.SetConnMaxLifetime(time.Hour): // Sets the maximum amount of time a connection can be reused before it’s closed. // A value of time.Hour means that each connection will be kept for up to 1 hour, after which it will be discarded and a new connection will be created if needed. DB = db }
Überprüfen Sie die Sicherung:
Stellen Sie sicher, dass die Sicherungsdatei erstellt wurde und überprüfen Sie ihre Größe oder öffnen Sie sie, um sicherzustellen, dass sie die erforderlichen Daten enthält.
Backups sicher speichern:
Bewahren Sie eine Kopie des Backups an einem sicheren Ort auf, z. B. in einem Cloud-Speicher oder auf einem separaten Server, um Datenverlust während des Rollback-Vorgangs zu verhindern.
Um Ihre MySQL-Daten zu sichern, wenn Sie Golang verwenden und auf AWS EKS bereitstellen, können Sie die folgenden Schritte ausführen:
Verwenden Sie mysqldump für die Datenbanksicherung:
Erstellen Sie mit einem Kubernetes-Cron-Job einen MySQL-Dump Ihrer MySQL-Datenbank.
package database import ( "golang-service/models" "golang-service/migrations" "gorm.io/gorm" ) func Migrate(db *gorm.DB) { db.AutoMigrate(&models.User{}) // Apply additional custom migrations if needed }
Speichern Sie dies in einem persistenten Volume oder einem S3-Bucket.
Automatisieren Sie mit Kubernetes CronJob:
Verwenden Sie einen Kubernetes CronJob, um den mysqldump-Prozess zu automatisieren.
Beispiel einer YAML-Konfiguration:yaml
package models import "gorm.io/gorm" type User struct { gorm.Model Name string `json:"name"` }
`
Verwenden von AWS RDS Automated Backups (bei Verwendung von RDS):
Wenn sich Ihre MySQL-Datenbank auf AWS RDS befindet, können Sie RDS automatisierte Backups und Snapshots nutzen.
Legen Sie einen Backup-Aufbewahrungszeitraum fest und erstellen Sie Snapshots manuell oder automatisieren Sie Snapshots mithilfe von Lambda-Funktionen.
Persistente Volumes (PV) mit Velero sichern:
Verwenden Sie Velero, ein Backup-Tool für Kubernetes, um das persistente Volume zu sichern, das MySQL-Daten enthält.
Installieren Sie Velero auf Ihrem EKS-Cluster und konfigurieren Sie es für die Sicherung auf S3.
Mit diesen Methoden können Sie sicherstellen, dass Ihre MySQL-Daten regelmäßig gesichert und sicher gespeichert werden.
Das obige ist der detaillierte Inhalt vonWarum ist die DB-Migration für Golang-Dienste wichtig?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!