Rumah  >  Artikel  >  pangkalan data  >  Migrasi DB Untuk Perkhidmatan Golang, Mengapa ia penting?

Migrasi DB Untuk Perkhidmatan Golang, Mengapa ia penting?

Patricia Arquette
Patricia Arquetteasal
2024-10-22 21:06:02975semak imbas

DB Migration For Golang Services, Why it matters?

DB Migration, 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:

  1. Evolusi Skema: Apabila aplikasi berkembang, model data mereka berubah. Migrasi membolehkan pembangun mengemas kini skema pangkalan data secara sistematik untuk mencerminkan perubahan ini, memastikan struktur pangkalan data sepadan dengan kod aplikasi.
  2. Kawalan Versi: Migrasi menyediakan cara untuk versi skema pangkalan data, membolehkan pasukan menjejaki perubahan dari semasa ke semasa. Versi ini membantu dalam memahami evolusi pangkalan data dan membantu dalam kerjasama antara pembangun.
  3. Ketekalan Merentas Persekitaran: Migrasi memastikan bahawa skema pangkalan data adalah konsisten merentas persekitaran yang berbeza (pembangunan, ujian, pengeluaran). Ini mengurangkan risiko percanggahan yang boleh membawa kepada pepijat dan isu penyepaduan.
  4. Keupayaan Kembalikan: Banyak alat migrasi menyokong perubahan semula, membenarkan pembangun kembali kepada keadaan pangkalan data sebelumnya jika migrasi menyebabkan masalah. Ini meningkatkan kestabilan semasa proses pembangunan dan penggunaan.
  5. Pengagihan Automatik: Migrasi boleh diautomasikan sebagai sebahagian daripada proses penempatan, memastikan perubahan skema yang diperlukan digunakan pada pangkalan data tanpa campur tangan manual. Ini menyelaraskan proses pelepasan dan mengurangkan ralat manusia.

Memohon dalam projek golang

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:

Struktur Projek

/golang-service
|-- main.go
|-- database
|   |-- migration.go
|-- models
|   |-- user.go
|-- config
|   |-- config.go
|-- migrations
|   |-- ...
|-- go.mod

1. Konfigurasi Pangkalan Data (config/config.go)

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
}

2. Migrasi Pangkalan Data (pangkalan data/migration.go)

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
}

3. Model (models/user.go)

package models

import "gorm.io/gorm"

type User struct {
    gorm.Model
    Name  string `json:"name"`
}

4. Konfigurasi Persekitaran (.env)

DB_USER=root
DB_PASS=yourpassword
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=yourdb

5. Titik Masuk Utama (main.go)

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)
}

6. Penjelasan:

  • Konfig Pangkalan Data: Mengurus pengumpulan sambungan untuk prestasi gred pengeluaran.
  • Fail Migrasi: (dalam folder migrasi) Membantu dalam versi skema pangkalan data.
  • Model GORM: Peta jadual pangkalan data kepada struct Go.
  • Penghijrahan Pangkalan Data: (dalam folder pangkalan data) Logik tersuai untuk mengubah jadual dari semasa ke semasa, membolehkan pemulangan mudah.
  • Pengujian: Anda boleh membuat ujian penyepaduan untuk persediaan ini menggunakan httptest dan memberi keterangan.

7. Buat Migrasi Pertama

  1. 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
    
  2. 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
    

8. Menukar skim

  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
    
  2. 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"`
    }
    
    

9. Penghijrahan Berbalik:

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)
}

10. Gunakan dengan Makefile

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.

11. Pertimbangan:

  • Kehilangan Data Semasa Rollback: Menggulung semula migrasi yang memadamkan lajur atau jadual boleh mengakibatkan kehilangan data, jadi sentiasa sandarkan data sebelum menjalankan rollback.
  • Penyatuan CI/CD: Sepadukan proses migrasi ke dalam saluran paip CI/CD anda untuk mengautomasikan perubahan skema semasa penggunaan.
  • Sandaran DB: Jadualkan sandaran pangkalan data biasa untuk mengelakkan kehilangan data sekiranya berlaku ralat pemindahan.

Mengenai sandaran DB

Sebelum melancarkan migrasi atau membuat perubahan yang berpotensi menjejaskan pangkalan data anda, berikut ialah beberapa perkara penting yang perlu dipertimbangkan.

  1. Perubahan Skema: Jika penghijrahan melibatkan pengubahsuaian skema (cth., menambah atau mengalih keluar lajur, menukar jenis data), bergulir semula ke migrasi sebelumnya boleh mengakibatkan kehilangan mana-mana data yang disimpan dalam lajur atau jadual yang diubah itu .
  2. Penyingkiran Data: Jika pemindahan termasuk perintah yang memadamkan data (seperti menjatuhkan jadual atau memotong jadual), berguling ke belakang akan melaksanakan penghijrahan "bawah" yang sepadan, yang boleh mengalih keluar data tersebut secara kekal.
  3. Pengendalian Transaksi: Jika alat migrasi anda menyokong urus niaga, pengembalian semula mungkin lebih selamat kerana perubahan digunakan dalam transaksi. Walau bagaimanapun, jika anda menjalankan perintah SQL secara manual di luar urus niaga, terdapat risiko kehilangan data.
  4. Integriti Data: Jika anda telah mengubah suai data mengikut cara yang bergantung pada skema semasa, gulung semula boleh menyebabkan pangkalan data anda dalam keadaan tidak konsisten.

Jadi, penting untuk membuat sandaran data anda. Berikut ialah panduan ringkas:

  1. 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.

  2. 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
    }
    
    
  3. Sahkan Sandaran:
    Pastikan fail sandaran telah dibuat dan semak saiznya atau bukanya untuk memastikan ia mengandungi data yang diperlukan.

  4. 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.

Sandaran pada awan

Untuk menyandarkan data MySQL anda apabila menggunakan Golang dan menggunakan pada AWS EKS, anda boleh mengikuti langkah berikut:

  1. 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.

  2. 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"`
    }
    
    


    `

  3. 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.

  4. 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!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel sebelumnya:Apakah Prosedur Tersimpan?Artikel seterusnya:Apakah Prosedur Tersimpan?