Rumah >pangkalan data >tutorial mysql >Migrasi DB Untuk Perkhidmatan Golang, Mengapa ia penting?
Pernahkah anda menghadapi situasi apabila anda menggunakan kemas kini baharu pada pengeluaran dengan skema pangkalan data yang dikemas kini, tetapi mendapat pepijat selepas itu dan perlu membalikkan perkara.... pada masa itulah penghijrahan berlaku.
Penghijrahan pangkalan data menyediakan beberapa tujuan utama:
Untuk mencipta persediaan gred pengeluaran yang komprehensif untuk perkhidmatan Golang menggunakan GORM dengan MySQL yang membolehkan penghijrahan, kemas kini dan pemulangan yang mudah, anda perlu menyertakan perkakas migrasi, mengendalikan pengumpulan sambungan pangkalan data dan memastikan definisi struktur yang betul. Berikut ialah contoh lengkap untuk membimbing anda melalui proses:
/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) }
Untuk persekitaran pengeluaran, kami boleh menggunakan pustaka migrasi seperti golang-migrate untuk memohon, tarik balik atau buat semula migrasi.
Pasang golang-migrate:
go install -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
Jana fail migrasi untuk jadual pengguna
migrate create -ext=sql -dir=./migrations -seq create_users_table
Selepas menjalankan arahan itu, kami akan mendapat sepasang .up.sql (untuk mengemas kini skema) dan down.sql (untuk kemungkinan rollback nanti) . Nombor 000001 ialah indeks penghijrahan yang dijana secara automatik.
/golang-service |-- migrations | |-- 000001_create_users_table.down.sql | |-- 000001_create_users_table.up.sql
Tambahkan arahan sql yang berkaitan pada fail .up , dan fail .down.
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;
Jalankan migrasi ke atas dan gunakan perubahan pada pangkalan data dengan arahan berikut (-bendera verbose untuk melihat lebih banyak butiran log):
migrate -path ./migrations -database "mysql://user:password@tcp(localhost:3306)/dbname" -verbose up
Sekiranya kami mendapat masalah dengan migrasi, kami boleh menggunakan arahan berikut untuk melihat versi migrasi semasa dan statusnya:
migrate -path ./migrations -database "mysql://user:password@tcp(localhost:3306)/dbname" version
Jika kami mengalami migrasi yang rosak atas sebab tertentu, kami boleh mempertimbangkan untuk menggunakan perintah force (gunakan dengan berhati-hati) dengan nombor versi migrasi kotor. Jika versi 1 (boleh menyemaknya dalam jadual migrasi atau schema_migrations), kami akan menjalankan:
migrate -path ./migrations -database "mysql://user:password@tcp(localhost:3306)/dbname" force 1
Pada satu ketika, kami mungkin ingin menambah ciri baharu dan sesetengah daripada ciri tersebut mungkin memerlukan perubahan skema data, contohnya kami ingin menambahkan medan e-mel pada jadual pengguna. Kami akan melakukannya seperti berikut.
Buat migrasi baharu untuk menambahkan lajur e-mel pada jadual pengguna
migrate create -ext=sql -dir=./migrations -seq add_email_to_users
Kini kami mempunyai pasangan baharu .up.sql dan .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
Menambah kandungan berikut pada *_add_email_to_users.*.sql fail
000002_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 }
Jalankan arahan migrasi ke atas sekali lagi untuk membuat kemas kini pada skema data
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 }
Kami juga perlu mengemas kini struct pengguna golang (menambah medan E-mel) untuk memastikan ia segerak dengan skema baharu..
package models import "gorm.io/gorm" type User struct { gorm.Model Name string `json:"name"` }
Sekiranya atas sebab tertentu kami mendapat pepijat dengan skema baharu yang dikemas kini dan kami perlu menarik balik, kes ini kami akan menggunakan arahan bawah:
DB_USER=root DB_PASS=yourpassword DB_HOST=127.0.0.1 DB_PORT=3306 DB_NAME=yourdb
Nombor 1 menunjukkan bahawa kami ingin menarik balik 1 penghijrahan.
Di sini kami juga perlu mengemas kini struct pengguna golang secara manual (alih keluar medan E-mel) untuk menggambarkan perubahan skema data.
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) }
Untuk memudahkan proses migrasi dan melancarkan semula, kami boleh menambah Makefile .
go install -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
Kandungan Makefile seperti berikut.
migrate create -ext=sql -dir=./migrations -seq create_users_table
Kini kita hanya boleh menjalankan make migrate_up atau membuat migrate_down pada CLI untuk melakukan migrasi dan rollback.
Sebelum melancarkan migrasi atau membuat perubahan yang berpotensi menjejaskan pangkalan data anda, berikut ialah beberapa perkara penting yang perlu dipertimbangkan.
Jadi, penting untuk membuat sandaran data anda. Berikut ialah panduan ringkas:
Pembuangan Pangkalan Data:
Gunakan alat khusus pangkalan data untuk membuat sandaran penuh pangkalan data anda. Untuk MySQL, anda boleh menggunakan:
/golang-service |-- main.go |-- database | |-- migration.go |-- models | |-- user.go |-- config | |-- config.go |-- migrations | |-- ... |-- go.mod
Ini mencipta fail (backup_before_rollback.sql) yang mengandungi semua data dan skema pangkalan data dbname.
Eksport Jadual Khusus:
Jika anda hanya perlu menyandarkan jadual tertentu, nyatakan dalam arahan mysqldump:
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 }
Sahkan Sandaran:
Pastikan fail sandaran telah dibuat dan semak saiznya atau bukanya untuk memastikan ia mengandungi data yang diperlukan.
Simpan Sandaran Dengan Selamat:
Simpan salinan sandaran di lokasi yang selamat, seperti storan awan atau pelayan yang berasingan, untuk mengelakkan kehilangan data semasa proses rollback.
Untuk menyandarkan data MySQL anda apabila menggunakan Golang dan menggunakan pada AWS EKS, anda boleh mengikuti langkah berikut:
Gunakan mysqldump untuk Sandaran Pangkalan Data:
Cipta mysqldump pangkalan data MySQL anda menggunakan tugas cron Kubernetes.
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 }
Simpan ini dalam kelantangan berterusan atau baldi S3.
Automasikan dengan Kubernetes CronJob:
Gunakan Kubernetes CronJob untuk mengautomasikan proses mysqldump.
Contoh konfigurasi YAML:yaml
package models import "gorm.io/gorm" type User struct { gorm.Model Name string `json:"name"` }
`
Menggunakan Sandaran Automatik AWS RDS (jika menggunakan RDS):
Jika pangkalan data MySQL anda berada di AWS RDS, anda boleh memanfaatkan RDS sandaran automatik dan syot kilat.
Tetapkan tempoh pengekalan sandaran dan ambil syot kilat secara manual atau automatikkan syot kilat menggunakan fungsi Lambda.
Sandarkan Jilid Berterusan (PV) dengan Velero:
Gunakan Velero, alat sandaran untuk Kubernetes, untuk menyandarkan volum berterusan yang menyimpan data MySQL.
Pasang Velero pada kelompok EKS anda dan konfigurasikannya untuk membuat sandaran kepada S3.
Dengan menggunakan kaedah ini, anda boleh memastikan data MySQL anda sentiasa disandarkan dan disimpan dengan selamat.
Atas ialah kandungan terperinci Migrasi DB Untuk Perkhidmatan Golang, Mengapa ia penting?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!