Maison > base de données > tutoriel mysql > le corps du texte

Migration de base de données pour les services Golang, pourquoi est-ce important ?

Patricia Arquette
Libérer: 2024-10-22 21:06:02
original
973 Les gens l'ont consulté

DB Migration For Golang Services, Why it matters?

Migration de base de données, pourquoi est-ce important ?

Avez-vous déjà été confronté à des situations où vous déployez une nouvelle mise à jour en production avec des schémas de base de données mis à jour, mais avez ensuite rencontré des bugs et devez annuler les choses... c'est à ce moment-là que la migration entre en place.

La migration de bases de données répond à plusieurs objectifs clés :

  1. Évolution du schéma : à mesure que les applications évoluent, leurs modèles de données changent. Les migrations permettent aux développeurs de mettre à jour systématiquement le schéma de la base de données pour refléter ces changements, garantissant ainsi que la structure de la base de données correspond au code de l'application.
  2. Contrôle de version : les migrations fournissent un moyen de versionner le schéma de la base de données, permettant aux équipes de suivre les modifications au fil du temps. Ce versioning aide à comprendre l'évolution de la base de données et facilite la collaboration entre les développeurs.
  3. Cohérence entre les environnements : les migrations garantissent que le schéma de la base de données est cohérent dans les différents environnements (développement, tests, production). Cela réduit le risque de divergences pouvant entraîner des bugs et des problèmes d'intégration.
  4. Capacité de restauration : de nombreux outils de migration prennent en charge l'annulation des modifications, permettant aux développeurs de revenir à un état antérieur de la base de données si une migration provoque des problèmes. Cela améliore la stabilité pendant le processus de développement et de déploiement.
  5. Déploiement automatisé : les migrations peuvent être automatisées dans le cadre du processus de déploiement, garantissant que les modifications de schéma nécessaires sont appliquées à la base de données sans intervention manuelle. Cela rationalise le processus de publication et réduit les erreurs humaines.

Postuler dans des projets Golang

Pour créer une configuration complète de niveau production pour un service Golang utilisant GORM avec MySQL qui permet des migrations, des mises à jour et des restaurations faciles, vous devez inclure des outils de migration, gérer le regroupement de connexions de base de données et garantir des définitions de structure appropriées. Voici un exemple complet pour vous guider tout au long du processus :

Structure du projet

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

Copier après la connexion
Copier après la connexion
Copier après la connexion

1. Configuration de la base de données (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
}

Copier après la connexion
Copier après la connexion
Copier après la connexion

2. Migration de base de données (database/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
}

Copier après la connexion
Copier après la connexion
Copier après la connexion

3. Modèles (models/user.go)

package models

import "gorm.io/gorm"

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

Copier après la connexion
Copier après la connexion
Copier après la connexion

4. Configuration de l'environnement (.env)

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

Copier après la connexion
Copier après la connexion

5. Point d'entrée principal (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)
}

Copier après la connexion
Copier après la connexion

6. Explication :

  • Configuration de la base de données : gère le regroupement de connexions pour des performances de niveau production.
  • Fichiers de migration : (dans le dossier migrations) Aide à la gestion des versions du schéma de base de données.
  • Modèles GORM : mappe les tables de base de données aux structures Go.
  • Migrations de bases de données : (dans le dossier de base de données) Logique personnalisée pour modifier les tables au fil du temps, permettant des restaurations faciles.
  • Tests : Vous pouvez créer des tests d'intégration pour cette configuration en utilisant httptest et témoigner.

7. Créer la première migration

  1. Pour les environnements de production, nous pourrions utiliser une bibliothèque de migration comme golang-migrate pour appliquer, annuler ou refaire des migrations.

    Installer golang-migrate :

    go install -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
    
    Copier après la connexion
    Copier après la connexion
  2. Générer les fichiers de migration pour le tableau des utilisateurs

    migrate create -ext=sql -dir=./migrations -seq create_users_table
    
    Copier après la connexion
    Copier après la connexion

    Après avoir exécuté la commande, nous obtiendrons une paire de .up.sql (pour mettre à jour le schéma) et down.sql (pour une éventuelle restauration ultérieure). Le nombre 000001 est l'index de migration généré automatiquement.

    /golang-service
    |-- migrations
    |   |-- 000001_create_users_table.down.sql
    |   |-- 000001_create_users_table.up.sql
    
    Copier après la connexion

    Ajoutez une commande SQL pertinente au fichier .up et au fichier .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);
    
    Copier après la connexion

    000001_create_users_table.down.sql

    DROP TABLE IF EXISTS users;
    
    Copier après la connexion

    Exécutez la migration vers le haut et appliquez les modifications à la base de données avec la commande suivante (indicateur -verbose pour voir plus de détails du journal) :

    migrate -path ./migrations -database "mysql://user:password@tcp(localhost:3306)/dbname" -verbose up
    
    Copier après la connexion

    Si nous rencontrons un problème avec la migration, nous pouvons utiliser la commande suivante pour voir la version actuelle de la migration et son statut :

    migrate -path ./migrations -database "mysql://user:password@tcp(localhost:3306)/dbname" version
    
    Copier après la connexion

    Si nous avons une migration interrompue pour certaines raisons, nous pouvons envisager d'utiliser la commande force (à utiliser avec précaution) avec le numéro de version de la migration sale. Si la version est 1 (on pourrait la vérifier dans la table migrations ou schema_migrations), nous exécuterions :

    migrate -path ./migrations -database "mysql://user:password@tcp(localhost:3306)/dbname" force 1
    
    Copier après la connexion

8. Modification des régimes

  1. À un moment donné, nous aimerions peut-être ajouter de nouvelles fonctionnalités et certaines d'entre elles pourraient nécessiter une modification des schémas de données, par exemple, nous aimerions ajouter un champ de courrier électronique à la table des utilisateurs. Nous le ferions comme suit.

    Effectuer une nouvelle migration pour ajouter la colonne email au tableau des utilisateurs

    migrate create -ext=sql -dir=./migrations -seq add_email_to_users
    
    Copier après la connexion

    Nous avons maintenant une nouvelle paire de .up.sql et .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
    
    Copier après la connexion
  2. Ajout du contenu suivant aux *_add_email_to_users.*.sql fichiers

    000002_add_email_to_users.up.sql

    /golang-service
    |-- main.go
    |-- database
    |   |-- migration.go
    |-- models
    |   |-- user.go
    |-- config
    |   |-- config.go
    |-- migrations
    |   |-- ...
    |-- go.mod
    
    
    Copier après la connexion
    Copier après la connexion
    Copier après la connexion

    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
    }
    
    
    Copier après la connexion
    Copier après la connexion
    Copier après la connexion

    Exécutez à nouveau la commande up migration pour mettre à jour les schémas de données

    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
    }
    
    
    Copier après la connexion
    Copier après la connexion
    Copier après la connexion

    Nous devrons également mettre à jour la structure des utilisateurs de Golang (en ajoutant le champ Email) pour la maintenir synchronisée avec les nouveaux schémas..

    package models
    
    import "gorm.io/gorm"
    
    type User struct {
        gorm.Model
        Name  string `json:"name"`
    }
    
    
    Copier après la connexion
    Copier après la connexion
    Copier après la connexion

9. Annulation des migrations :

Si, pour une raison quelconque, nous rencontrons des bugs avec les nouveaux schémas mis à jour et que nous devons annuler, dans ce cas, nous utiliserons la commande down :

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

Copier après la connexion
Copier après la connexion

Le numéro 1 indique que nous souhaitons annuler 1 migration.

Ici, nous devons également mettre à jour manuellement la structure des utilisateurs de Golang (supprimer le champ E-mail) pour refléter les modifications du schéma de données.

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

Copier après la connexion
Copier après la connexion

10. Utiliser avec Makefile

Pour simplifier le processus de migration et de restauration, nous pouvons ajouter un Makefile .

go install -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
Copier après la connexion
Copier après la connexion

Le contenu de Makefile comme suit.

migrate create -ext=sql -dir=./migrations -seq create_users_table
Copier après la connexion
Copier après la connexion

Maintenant, nous pouvons simplement exécuter make migrate_up ou make migrate_down sur CLI pour effectuer la migration et la restauration.

11.Considérations :

  • Perte de données lors de la restauration : l'annulation des migrations qui suppriment des colonnes ou des tables peut entraîner une perte de données, donc toujours sauvegarde les données avant d'effectuer une restauration.
  • Intégration CI/CD : intégrez le processus de migration dans votre pipeline CI/CD pour automatiser les modifications de schéma pendant le déploiement.
  • Sauvegardes de base de données : planifiez des sauvegardes régulières de la base de données pour éviter la perte de données en cas d'erreurs de migration.

À propos des sauvegardes de base de données

Avant d'annuler une migration ou d'apporter des modifications susceptibles d'affecter votre base de données, voici quelques points clés à considérer.

  1. Modifications du schéma : si la migration impliquait une modification du schéma (par exemple, ajout ou suppression de colonnes, modification des types de données), le retour à une migration précédente peut entraîner la perte de toutes les données stockées dans ces colonnes ou tables modifiées. .
  2. Suppression des données : si la migration inclut des commandes qui suppriment des données (comme la suppression de tables ou la troncature de tables), la restauration exécutera la migration « vers le bas » correspondante, ce qui pourrait supprimer définitivement ces données.
  3. Gestion des transactions : si votre outil de migration prend en charge les transactions, la restauration peut être plus sûre puisque les modifications sont appliquées dans une transaction. Cependant, si vous exécutez manuellement des commandes SQL en dehors des transactions, vous risquez de perdre des données.
  4. Intégrité des données : si vous avez modifié les données d'une manière qui dépend du schéma actuel, la restauration pourrait laisser votre base de données dans un état incohérent.

Il est donc crucial de sauvegarder vos données. Voici un bref guide :

  1. Dump de base de données :
    Utilisez des outils spécifiques à la base de données pour créer une sauvegarde complète de votre base de données. Pour MySQL, vous pouvez utiliser :

    /golang-service
    |-- main.go
    |-- database
    |   |-- migration.go
    |-- models
    |   |-- user.go
    |-- config
    |   |-- config.go
    |-- migrations
    |   |-- ...
    |-- go.mod
    
    
    Copier après la connexion
    Copier après la connexion
    Copier après la connexion

    Cela crée un fichier (backup_before_rollback.sql) qui contient toutes les données et le schéma de la base de données dbname.

  2. Exporter des tableaux spécifiques :
    Si vous n'avez besoin de sauvegarder que certaines tables, spécifiez-les dans la commande 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
    }
    
    
    Copier après la connexion
    Copier après la connexion
    Copier après la connexion
  3. Vérifier la sauvegarde :
    Assurez-vous que le fichier de sauvegarde a été créé et vérifiez sa taille ou ouvrez-le pour vous assurer qu'il contient les données nécessaires.

  4. Stockez les sauvegardes en toute sécurité :
    Conservez une copie de la sauvegarde dans un emplacement sécurisé, tel qu'un stockage cloud ou un serveur distinct, pour éviter la perte de données pendant le processus de restauration.

Sauvegardes sur le cloud

Pour sauvegarder vos données MySQL lorsque vous utilisez Golang et que vous les déployez sur AWS EKS, vous pouvez suivre ces étapes :

  1. Utilisez mysqldump pour la sauvegarde de la base de données :
    Créez un mysqldump de votre base de données MySQL à l'aide d'une tâche 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
    }
    
    
    Copier après la connexion
    Copier après la connexion
    Copier après la connexion

    Stockez-le dans un volume persistant ou un compartiment S3.

  2. Automatiser avec Kubernetes CronJob :
    Utilisez un CronJob Kubernetes pour automatiser le processus mysqldump.
    Exemple de configuration YAML :yaml

    package models
    
    import "gorm.io/gorm"
    
    type User struct {
        gorm.Model
        Name  string `json:"name"`
    }
    
    
    Copier après la connexion
    Copier après la connexion
    Copier après la connexion


    `

  3. Utilisation des sauvegardes automatisées AWS RDS (si vous utilisez RDS) :
    Si votre base de données MySQL est sur AWS RDS, vous pouvez tirer parti des sauvegardes et des instantanés automatisés RDS.
    Définissez une période de conservation des sauvegardes et prenez des instantanés manuellement ou automatisez les instantanés à l'aide des fonctions Lambda.

  4. Sauvegarder les volumes persistants (PV) avec Velero :
    Utilisez Velero, un outil de sauvegarde pour Kubernetes, pour sauvegarder le volume persistant contenant les données MySQL.
    Installez Velero sur votre cluster EKS et configurez-le pour sauvegarder sur S3.

En utilisant ces méthodes, vous pouvez vous assurer que vos données MySQL sont régulièrement sauvegardées et stockées en toute sécurité.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!