Golang中如何实现数据备份与恢复

Golang中如何实现数据备份与恢复 image

如何使用 Go 语言进行备份和恢复? 备份格式支持 JSON、CSV、PDF 和其他格式吗?

2 回复

你所说的“备份”是什么意思?

更多关于Golang中如何实现数据备份与恢复的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中实现数据备份与恢复可以通过多种方式实现,具体取决于数据源和备份格式需求。以下是一些常见实现方案:

1. 数据库备份与恢复

PostgreSQL备份示例:

package main

import (
    "database/sql"
    "fmt"
    "os"
    "os/exec"
    _ "github.com/lib/pq"
)

// 备份PostgreSQL数据库
func backupPostgreSQL(dbname, user, password, host string, port int) error {
    dumpFile := fmt.Sprintf("%s_backup.sql", dbname)
    cmd := exec.Command("pg_dump",
        "-h", host,
        "-p", fmt.Sprintf("%d", port),
        "-U", user,
        "-d", dbname,
        "-f", dumpFile)
    
    cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", password))
    
    return cmd.Run()
}

// 恢复PostgreSQL数据库
func restorePostgreSQL(dbname, user, password, host string, port int, backupFile string) error {
    cmd := exec.Command("psql",
        "-h", host,
        "-p", fmt.Sprintf("%d", port),
        "-U", user,
        "-d", dbname,
        "-f", backupFile)
    
    cmd.Env = append(os.Environ(), fmt.Sprintf("PGPASSWORD=%s", password))
    
    return cmd.Run()
}

2. 结构化数据备份(JSON/CSV)

JSON备份示例:

package main

import (
    "encoding/json"
    "os"
    "time"
)

type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    CreatedAt time.Time `json:"created_at"`
}

// 备份到JSON文件
func backupToJSON(data []User, filename string) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    encoder := json.NewEncoder(file)
    encoder.SetIndent("", "  ")
    return encoder.Encode(data)
}

// 从JSON文件恢复
func restoreFromJSON(filename string) ([]User, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    var users []User
    decoder := json.NewDecoder(file)
    err = decoder.Decode(&users)
    return users, err
}

CSV备份示例:

package main

import (
    "encoding/csv"
    "os"
    "strconv"
    "time"
)

// 备份到CSV文件
func backupToCSV(data []User, filename string) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    writer := csv.NewWriter(file)
    defer writer.Flush()
    
    // 写入表头
    header := []string{"ID", "Name", "Email", "CreatedAt"}
    if err := writer.Write(header); err != nil {
        return err
    }
    
    // 写入数据
    for _, user := range data {
        record := []string{
            strconv.Itoa(user.ID),
            user.Name,
            user.Email,
            user.CreatedAt.Format(time.RFC3339),
        }
        if err := writer.Write(record); err != nil {
            return err
        }
    }
    
    return nil
}

// 从CSV文件恢复
func restoreFromCSV(filename string) ([]User, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    reader := csv.NewReader(file)
    records, err := reader.ReadAll()
    if err != nil {
        return nil, err
    }
    
    var users []User
    for i, record := range records {
        if i == 0 { // 跳过表头
            continue
        }
        
        id, _ := strconv.Atoi(record[0])
        createdAt, _ := time.Parse(time.RFC3339, record[3])
        
        user := User{
            ID:        id,
            Name:      record[1],
            Email:     record[2],
            CreatedAt: createdAt,
        }
        users = append(users, user)
    }
    
    return users, nil
}

3. PDF备份(使用第三方库)

package main

import (
    "github.com/jung-kurt/gofpdf"
    "os"
)

// 备份到PDF文件
func backupToPDF(data []User, filename string) error {
    pdf := gofpdf.New("P", "mm", "A4", "")
    pdf.AddPage()
    pdf.SetFont("Arial", "B", 16)
    pdf.Cell(40, 10, "用户数据备份")
    pdf.Ln(20)
    
    pdf.SetFont("Arial", "", 12)
    for _, user := range data {
        pdf.Cell(0, 10, 
            fmt.Sprintf("ID: %d, 姓名: %s, 邮箱: %s", 
                user.ID, user.Name, user.Email))
        pdf.Ln(10)
    }
    
    return pdf.OutputFileAndClose(filename)
}

4. 通用文件备份系统

package main

import (
    "archive/zip"
    "crypto/sha256"
    "encoding/hex"
    "io"
    "os"
    "path/filepath"
    "time"
)

// 创建备份压缩包
func createBackupArchive(sourceDir, backupDir string) (string, error) {
    timestamp := time.Now().Format("20060102_150405")
    backupFile := filepath.Join(backupDir, fmt.Sprintf("backup_%s.zip", timestamp))
    
    zipFile, err := os.Create(backupFile)
    if err != nil {
        return "", err
    }
    defer zipFile.Close()
    
    zipWriter := zip.NewWriter(zipFile)
    defer zipWriter.Close()
    
    // 计算文件哈希用于验证
    hasher := sha256.New()
    
    err = filepath.Walk(sourceDir, func(filePath string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        
        if info.IsDir() {
            return nil
        }
        
        relPath, _ := filepath.Rel(sourceDir, filePath)
        zipEntry, err := zipWriter.Create(relPath)
        if err != nil {
            return err
        }
        
        file, err := os.Open(filePath)
        if err != nil {
            return err
        }
        defer file.Close()
        
        multiWriter := io.MultiWriter(zipEntry, hasher)
        _, err = io.Copy(multiWriter, file)
        return err
    })
    
    if err != nil {
        return "", err
    }
    
    // 保存哈希值用于验证
    hash := hex.EncodeToString(hasher.Sum(nil))
    hashFile := backupFile + ".sha256"
    os.WriteFile(hashFile, []byte(hash), 0644)
    
    return backupFile, nil
}

// 恢复备份
func restoreBackup(backupFile, targetDir string) error {
    zipReader, err := zip.OpenReader(backupFile)
    if err != nil {
        return err
    }
    defer zipReader.Close()
    
    for _, file := range zipReader.File {
        filePath := filepath.Join(targetDir, file.Name)
        
        if file.FileInfo().IsDir() {
            os.MkdirAll(filePath, os.ModePerm)
            continue
        }
        
        if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
            return err
        }
        
        dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
        if err != nil {
            return err
        }
        defer dstFile.Close()
        
        srcFile, err := file.Open()
        if err != nil {
            return err
        }
        defer srcFile.Close()
        
        _, err = io.Copy(dstFile, srcFile)
        if err != nil {
            return err
        }
    }
    
    return nil
}

5. 增量备份实现

package main

import (
    "crypto/md5"
    "encoding/hex"
    "io"
    "os"
    "path/filepath"
    "time"
)

type BackupEntry struct {
    FilePath    string    `json:"file_path"`
    Hash        string    `json:"hash"`
    Size        int64     `json:"size"`
    Modified    time.Time `json:"modified"`
    BackupTime  time.Time `json:"backup_time"`
}

// 增量备份
func incrementalBackup(sourceDir, backupDir string, lastBackup map[string]BackupEntry) ([]BackupEntry, error) {
    var changedFiles []BackupEntry
    
    err := filepath.Walk(sourceDir, func(filePath string, info os.FileInfo, err error) error {
        if err != nil || info.IsDir() {
            return err
        }
        
        // 计算文件哈希
        hash, err := calculateFileHash(filePath)
        if err != nil {
            return err
        }
        
        relPath, _ := filepath.Rel(sourceDir, filePath)
        
        // 检查文件是否变化
        if lastEntry, exists := lastBackup[relPath]; exists {
            if lastEntry.Hash == hash && lastEntry.Size == info.Size() &&
               lastEntry.Modified.Equal(info.ModTime()) {
                return nil // 文件未变化,跳过
            }
        }
        
        // 备份变化文件
        backupPath := filepath.Join(backupDir, relPath)
        if err := os.MkdirAll(filepath.Dir(backupPath), os.ModePerm); err != nil {
            return err
        }
        
        if err := copyFile(filePath, backupPath); err != nil {
            return err
        }
        
        changedFiles = append(changedFiles, BackupEntry{
            FilePath:   relPath,
            Hash:       hash,
            Size:       info.Size(),
            Modified:   info.ModTime(),
            BackupTime: time.Now(),
        })
        
        return nil
    })
    
    return changedFiles, err
}

func calculateFileHash(filePath string) (string, error) {
    file, err := os.Open(filePath)
    if err != nil {
        return "", err
    }
    defer file.Close()
    
    hasher := md5.New()
    if _, err := io.Copy(hasher, file); err != nil {
        return "", err
    }
    
    return hex.EncodeToString(hasher.Sum(nil)), nil
}

func copyFile(src, dst string) error {
    source, err := os.Open(src)
    if err != nil {
        return err
    }
    defer source.Close()
    
    destination, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer destination.Close()
    
    _, err = io.Copy(destination, source)
    return err
}

Go语言支持多种备份格式:

  • JSON:使用标准库encoding/json
  • CSV:使用标准库encoding/csv
  • PDF:需要使用第三方库如gofpdffpdf
  • SQL:通过执行数据库工具命令
  • 二进制/压缩格式:使用archive/zipcompress/gzip等标准库

备份策略可根据需求选择全量备份、增量备份或差异备份。关键是要确保备份数据的完整性和可恢复性,建议实现备份验证机制。

回到顶部