MySQL-5.7版本数据库全量备份golang脚本


前言

数据库备份与恢复是数据库管理中最重要的方面之一。如果数据库崩溃后却没有办法恢复,那么对企业造成的毁灭性结果可能会是数据丢失、收入减少、客户不满等。不管公司是使用单个数据库还是多个数据库来存储数百 GB 或 TB 的数据,它们都有一个共同点,即需要有一个备份与恢复方案或脚本来备份重要数据并使自身免于灾难。
以下脚本主要用于全量备份,已验证MySQL-5.7版本可正常使用,如果了解golang语言,可以轻松看懂该脚本,有不对的地方请大佬们指教!!!


一、数据库备份

1、备份方式

	数据库备份一般有逻辑备份与物理备份:
		逻辑备份包含使用 mysqldump 命令导出并存储在二进制文件/SQL文件中的数据,只能还原至备份的时间点,例如mysqldump,操作过程中会锁表。
		
		物理备份是物理数据库文件的副本,可以利用全备份加增量binlog备份还原至任意时间点,例如xtrabackup,操作过程中不会锁表。

2、备份工具

	Percona Xtrabackup是一款针对MySQL数据库的开源、免费的热备工具,支持增量备份,压缩备份和流备份等,功能十分强大,
	主要有有以下优点:
		1.备份速度快,且可靠
		2.备份过程中不会打断正在执行的事务
		3.能够基于压缩等功能节约磁盘空间和流量
		4.自动备份校验
		5.备份还原的速度更快
		6.可以使用流备,将备份传输到另一台机器 

二、备份脚本解析

详细golang脚本见下载

0、前言

	原本该脚本是一个简单的shell脚本,执行起来简单轻巧,且shell脚本可读性强。
	
	为什么会写出一版golang形式的脚本?
		主要是因为目前正在学习golang语言,为了更好的学习golang中的相关语法及golang的标准库使用方法,因此才对shell脚本进行了改写。

1、脚本目录详情

在这里插入图片描述

2、configs配置文件解读

程序在启动后,通过golang的os标准库首先读取到对应的配置文件,将其反序列化为struct结构体形式,并对yaml文件中的模板变量进行解析,详情见utils.go文件

代码如下(示例):

username: "xx"
password: "xxxx"
host: "xx.xx.xx.xxx"
mysql_port: "xx"
expired_day: "{{ .expired_day }}"  #yaml文件中指定模板变量
current_day: "{{ .current_day }}"
backup_data_path: "/xx/xx/xx/xx/my{{ .mysql_port }}/xtrabackup/data"
backup_log_path: "/xx/xx/xx/xx/my{{ .mysql_port }}/xtrabackup/log"
backup_binlog_path: "/xx/xx/xx/xx/my{{ .mysql_port }}/xtrabackup/binlog"
backup_log_file: "full_backup_data_{{ .current_day }}.log"
backup_binlog_file: "full_backup_binlog_{{ .current_day }}.log"
binlog_pathfile_src: "/xx/xx/xx/my{{ .mysql_port }}/binlog"
innobackupex: "/xx/xx/innobackupex"
xtrbackup: "/xx/xx/xtrabackup"
mycnf: "/xx/xx/xx/my{{ .mysql_port }}/my.cnf"
mysock: "/xx/xx/xx/my{{ .mysql_port }}/run/mysqld.sock"
parallel: "4"
username数据库备份用户,需要对所有库表拥有权限
password数据库备份用户密码
host数据库地址
mysql_port数据库端口
expired_day备份过期删除时间(即保留7天的备份)
current_day当前时间
backup_data_path备份数据存放目录
backup_log_path备份日志存放目录
backup_binlog_pathbinlog备份存放目录
backup_log_file备份日志存放位置
backup_binlog_filebinlog备份存放位置
binlog_pathfile_src生成binlog的位置
innobackupexinnobackupex备份工具命令位置(备份并未使用它)
xtrbackupxtrbackup备份工具命令位置
mycnf数据库配置文件存放位置
mysock数据库sock文件存放位置
parallel备份时指定多线程数量

3、Init初始化目录解读

该目录下的init.go文件主要用于备份前进行相关初始化操作,例如:数据库连接是否正常检测、yaml配置文件加载
首先导入标准库 go get database/sql

package Init

import (
	"MySQL_BackUp/service"
	"MySQL_BackUp/utils"
	"database/sql"
	"fmt"
	"log"
	"time"
)

// Init 函数用于初始化服务,包括检查数据库连接、yaml模板文件加载
func Init(yamlFilePath string) (*service.MySQLBackupService, error) {
	// 加载基本配置(拿到yaml文件中的相关配置)
	config, err := utils.LoadConfig(yamlFilePath, nil)
	if err != nil {
		log.Fatalf("failed to load config: %v", err)
	}

	/*
		给yaml文件中的三个模板变量赋值
	*/
	// 获取当前日期
	currentDay := time.Now().Format("2006-01-02")
	// 假设过期日期是当前日期后7天
	expiredDay := time.Now().AddDate(0, 0, 7).Format("2006-01-09")
	data := map[string]string{
		"current_day": currentDay,
		"expired_day": expiredDay,
		"mysql_port":  config.MySQLPort,
	}

	// 再次加载配置,替换掉yaml中的模板变量 {{ . xxx }}
	finalConfig, err := utils.LoadConfig(yamlFilePath, data)
	if err != nil {
		log.Fatalf("failed to open database connection: %v", err)
	}

	/*
		1、检查数据库连接--连接数据库自带的mysql库验证即可
		2、创建数据库对象
		3、进行ping操作,验证数据库通信是否正常
	*/
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s",
		finalConfig.Username, finalConfig.Password, finalConfig.Host, finalConfig.MySQLPort, "mysql")
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		log.Fatalf("failed to open database connection: %v", err)
	}

	if err = db.Ping(); err != nil {
		log.Fatalf("failed to ping database: %v", err)
	}
	log.Printf("mysql connect success: %v", dsn)

	// 初始化 MySQLBackupService 实例,并返回该对象
}

这段代码主要引用了自己写的utils.go文件,针对yaml文件中的模板变量进行解析并赋值

4、utils工具目录解读

主要引用到了gopkg.in/yaml.v2、text/template标准库的用法,具体使用详情自行学习即可

package utils

import (
	"MySQL_BackUp/models"
	"bytes"
	"fmt"
	"os"
	"text/template"

	"gopkg.in/yaml.v2"
)

/*
	用于解决yaml文件包含{{ .xxx }}这种模板变量的

	主要思想就是读取 YAML 文件并将其内容解析为包含 结构体,然后结合"text/template" 标准库对其模板变量进行解析赋值
*/
// 用于解析和填充配置模板字符串
func ParseConfigTemplate(configStr string, data map[string]string) (string, error) {
	/*
		template.New("config") 创建一个新的模板对象,其名称为 "config"
		Parse(configStr) 方法解析传入的模板字符串 configStr。
	*/
	tmpl, err := template.New("config").Parse(configStr)
	if err != nil {
		return "", err
	}
	/*
		var buf bytes.Buffer 创建一个缓冲区,用于存储模板执行后的输出结果。
		tmpl.Execute(&buf, data) 方法将模板与数据结合并将结果写入 buf 中。
			&buf 是指向缓冲区的指针。
			data 是包含要填充模板的数据的映射。
		将缓冲区中的内容转换为字符串,并且返回结果和 nil 表示没有错误
	*/
	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, data); err != nil {
		return "", err
	}

	return buf.String(), nil
}

// 读取yaml文件并进行模板加载
func LoadConfig(yamlFilePath string, data map[string]string) (*models.Config, error) {
	// 读取yaml文件
	configFile, err := os.ReadFile(yamlFilePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read config file: %w", err)
	}

	/*
		yaml.Unmarshal(configFile, &config) 将 YAML 文件的内容反序列化为 models.Config 结构体。
		如果反序列化过程中出现错误,则返回相应的错误信息。
	*/
	var config models.Config
	err = yaml.Unmarshal(configFile, &config)
	if err != nil {
		return nil, fmt.Errorf("failed to unmarshal config: %w", err)
	}

	// yaml文件中包含 {{ .xxx }} 模板变量的结构体字段
	fields := []*string{
		&config.ExpiredDay,
		&config.CurrentDay,
		&config.BackupPath,
		&config.LogPath,
		&config.BinlogPath,
		&config.LogFile,
		&config.BinlogFile,
		&config.BinlogPathFileSrc,
		&config.MyCnf,
		&config.MySock,
	}
	/*
		对于结构体中的每个字段,都使用 ParseConfigTemplate 函数处理其中可能包含的模板变量。
		对字段的值进行了处理,并将处理后的值赋回到原始的结构体字段中。
		如果处理过程中出现错误,则返回相应的错误信息。
	*/
	for _, field := range fields {
		processedValue, err := ParseConfigTemplate(*field, data)
		if err != nil {
			return nil, fmt.Errorf("failed to process template: %w", err)
		}
		*field = processedValue
	}

	return &config, nil
}

5、models目录解读

package models

//与yaml文件中的变量一致,使用了结构体中的tag标签,将结构体解析为对应的yaml文件名称
type Config struct {
	Username          string `yaml:"username"`
	Password          string `yaml:"password"`
	Host              string `yaml:"host"`
	MySQLPort         string `yaml:"mysql_port"`
	ExpiredDay        string `yaml:"expired_day"`
	CurrentDay        string `yaml:"current_day"`
	BackupPath        string `yaml:"backup_data_path"`
	LogPath           string `yaml:"backup_log_path"`
	BinlogPath        string `yaml:"backup_binlog_path"`
	LogFile           string `yaml:"backup_log_file"`
	BinlogPathFileSrc string `yaml:"binlog_pathfile_src"`
	BinlogFile        string `yaml:"backup_binlog_file"`
	Innobackupex      string `yaml:"innobackupex"`
	Xtrabackup        string `yaml:"xtrbackup"`
	MyCnf             string `yaml:"mycnf"`
	MySock            string `yaml:"mysock"`
	Parallel          string `yaml:"parallel"`
}

// 定义相关操作方法存放到接口中,只有实现了接口中方法,才等于实现了该接口
type BackupService interface {
	CheckAndCreateDirectories(directories []string, binlogFile string) error //创建目录
	CheckXtarPid() bool                                                      //检查是否存在备份进程
	CheckDevUsage()                                                          //检查磁盘使用率
	BackupDatabase()                                                         //全量备份
	BackupBinlog()                                                           //binlog备份
}

6、service目录解读

这个目录下主要实现了上述目录中的接口

package service

import (
	"MySQL_BackUp/models"
	"database/sql"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/shirou/gopsutil/disk"
)

// 定义结构体
type MySQLBackupService struct {
	Config *models.Config
	Db     *sql.DB
}

// 检查备份路径是否存在,如果不存在则创建
func (b *MySQLBackupService) CheckAndCreateDirectories(directories []string) error {
	for _, dir := range directories {
		//获取目录信息,并判断目录是否存在,如果不存在则创建
		if _, err := os.Stat(dir); os.IsNotExist(err) {
			err := os.MkdirAll(dir, os.ModePerm)
			if err != nil {
				return fmt.Errorf("failed to create directory %s: %v", dir, err)
			}
			log.Printf("Directory %s created\n", dir)
		} else {
			log.Printf("Directory %s already exists\n", dir)
		}
	}
	return nil
}

// 检查当前是否存在备份进程
func (b *MySQLBackupService) CheckXtarPid() bool {
	//golang执行shell命令标准库 exec.Command("xx","xx")创建新的*cmd对象
	cmd := exec.Command("pgrep", "-f", "xtra")
	// 使用Output方法执行该命令并收集其输出
	output, err := cmd.Output()
	if err == nil && len(output) > 0 {
		log.Printf("# 当前存在备份进程:\n%s", output)
		return true
	} else {
		return false
	}
}
//检查数据库存储备份数据文件的磁盘大小
func (b *MySQLBackupService) CheckDevUsage() {
	//使用第三方disk库文件获取磁盘使用率
	usageStat, err := disk.Usage(b.Config.BackupPath)
	if err != nil {
		log.Fatalf("Failed to get disk usage: %v", err)
	}
	devUsage := usageStat.UsedPercent

	//判断磁盘使用率是否超过了80%
	if devUsage >= 80 {
		log.Printf("# disk dev_usage more than 80%%: %.2f\n", devUsage)
		//遍历执行目录下的所有文件 filepath.Walk()函数本身具有递归调用功能
		err := filepath.Walk(b.Config.BackupPath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			//判断指定目录下的文件及文件日期
			if !info.IsDir() && time.Since(info.ModTime()).Hours() > 24*7 {
				if err := os.Remove(path); err != nil {
					return err
				}
			}

			return nil
		})

		if err != nil {
			log.Fatalf("# failed to delete old backup files: %s\n", err.Error())
		}
		log.Printf("# delete backup files older than 7 days\n")
	}
}
// 执行全量备份
func (b *MySQLBackupService) BackupDatabase() {
	//判断备份文件是否存在,如果不存在则创建
	log.Printf("# 开始全备份...")
	backupFile := fmt.Sprintf("%s/full_%s.tar.gz", b.Config.BackupPath, time.Now().Format("20060102"))
	file, err := os.Create(backupFile)
	if err != nil {
		log.Fatalf("# failed to create backup file: %s\n", err.Error())
	}
	defer file.Close()

	/*
		1、拼接日志文件路径
		2、使用os.OpenFile()函数打开日志文件,如果不存在则创建
	*/
	logFilePath := filepath.Join(b.Config.LogPath, b.Config.LogFile)
	logFile, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("# failed to open log file: %s\n", err.Error())
	}
	defer logFile.Close()
	/*
		执行xtrbackup备份命令,并启用多线程
		 $xtrabackup --defaults-file=${mycnf} --user=$username --password=$passwd --slave-info   --backup  --lock-ddl-per-table --stream=tar  2>> $log_file | gzip - > ${backup_path}/full_${current_day}.tar.gz
	*/
	cmd := exec.Command(b.Config.Xtrabackup,
		"--defaults-file="+b.Config.MyCnf,
		"--user="+b.Config.Username,
		"--password="+b.Config.Password,
		"--slave-info",
		"--backup",
		"--lock-ddl-per-table",
		"--stream=tar",
		"--parallel="+b.Config.Parallel,
	)

	//执行gzip压缩命令
	gzipCmd := exec.Command("gzip")

	//因为原备份命令是多命令组合而成的,因此使用管道 cmd.StdoutPipe()
	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatalf("# failed to get stdout pipe: %s\n", err.Error())
	}
	defer stdoutPipe.Close()

	//执行命令,并区分stdin、stdout 和 stderr
	/*
		这行代码将gzipCmd命令的标准输入(stdin)设置为另一个命令的标准输出管道(stdoutPipe)。
		stdoutPipe是一个io.Reader,通常由前一个命令创建,用于从其标准输出读取数据。
		这样做的目的是将前一个命令的输出直接作为gzipCmd的输入,实现数据流的连接。
	*/
	gzipCmd.Stdin = stdoutPipe
	gzipCmd.Stdout = file    //标准输出 到备份文件backupfile中
	cmd.Stderr = logFile     //备份命令标准错误  输出到日志文件full_backup_data_{{ .current_day }}.log
	gzipCmd.Stderr = logFile //gzip压缩命令标准错误  输出到日志文件full_backup_data_{{ .current_day }}.log

	/*
			cmd.Run()和cmd.Start()的区别:
				cmd.Run() 执行 Run 会立即阻塞当前goroutine 等待 5 秒种
				Run() 方法适用于需要顺序执行外部命令

		 		cmd.Start() 不用等命令执行完成,就结束, 然后在 Wait() 方法阻塞等待 5s
				使用 Start() 方法适用于需要并发执行多个命令的情况,
				wait()方法必须与start结合使用
	*/
}

// 备份binlog
func (b *MySQLBackupService) BackupBinlog() {
	log.Printf("# 开始增量备份binlog...")
	binlogFilePath := filepath.Join(b.Config.BinlogPath, b.Config.BinlogFile)
	binlogFile, err := os.OpenFile(binlogFilePath, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("# failed to open binlog file: %s\n", err.Error())
	}
	defer binlogFile.Close()

	// 函数写入日志
	writeLog := func(message string) {
		log.Println(message)
		binlogFile.WriteString(message + "\n")
	}

	// 查找所有匹配的binlog文件,并按升序排序
	cmd1 := exec.Command("sh", "-c", fmt.Sprintf("for i in $(find %s -name 'mysql-bin.*' | grep -v mysql-bin.index | sort -n | sed '$d'); do if [ ! -e %s/$(basename $i) ]; then cp -p $i %s; cp -p $i %s; fi; done", b.Config.BinlogPathFileSrc, b.Config.BinlogPath, b.Config.BinlogPath, b.Config.BinlogPath))
	output1, err := cmd1.CombinedOutput()
	if err != nil {
		writeLog(fmt.Sprintf("# %s 备份binlog失败: %s\n%s", time.Now().Format("2006-01-02 15:04:05"), err.Error(), output1))
	} else {
		writeLog(fmt.Sprintf("# %s 备份binlog成功:\n%s", time.Now().Format("2006-01-02 15:04:05"), output1))
	}

	// 删除过期备份binlog数据,只保留一个备份周期7天的数据
	deleteCmd := exec.Command("find", b.Config.BinlogPath, "-name", "mysql-bin.*", "-ctime", "+6", "-exec", "rm", "-f", "{}", ";")
	output3, err := deleteCmd.CombinedOutput()
	if err != nil {
		writeLog(fmt.Sprintf("# %s 删除过期备份binlog数据失败: %s\n%s", time.Now().Format("2006-01-02 15:04:05"), err.Error(), output3))
	} else {
		writeLog(fmt.Sprintf("# %s 删除过期备份binlog数据成功:\n%s", time.Now().Format("2006-01-02 15:04:05"), output3))
	}

	writeLog("# 增量备份完毕...")
}

7、cmd目录解读

此处是程序的入口处

package main

import (
	"MySQL_BackUp/Init"
	"fmt"
	"log"
	"os"
	"sync"

	_ "github.com/go-sql-driver/mysql"
)

// 程序入口
func main() {
	//命令行参数 ./xxxx config.yaml <-- yamlFilePath
	if len(os.Args) < 2 {
		fmt.Println("Usage: ./mysql_backup <yaml_file_path>")
		os.Exit(1)
	}
	yamlFilePath := os.Args[1]

	// 初始化服务
	backupService, err := Init.Init(yamlFilePath)
	if err != nil {
		log.Fatalf("Initialization failed: %v", err)
	}
	//延迟关闭数据库
	defer backupService.Db.Close()

	//定义并检查BackupPath、LogPath、BinlogPath路径是否存在
	directories := []string{backupService.Config.BackupPath, backupService.Config.LogPath, backupService.Config.BinlogPath}
	if err := backupService.CheckAndCreateDirectories(directories); err != nil {
		log.Fatalf("Failed to check and create directories: %v", err)
	}

	//检查是否存在备份进程
	if backupService.CheckXtarPid() {
		log.Println("# 备份程序退出~~")
		os.Exit(1)
	}
	var wg sync.WaitGroup
	var mu sync.Mutex

	// 检查磁盘空间
	wg.Add(1)
	go func() {
		defer wg.Done()
		mu.Lock()
		defer mu.Unlock()
		backupService.CheckDevUsage()
	}()

	// 全量备份数据库操作
	wg.Add(1)
	go func() {
		defer wg.Done()
		mu.Lock()
		defer mu.Unlock()
		backupService.BackupDatabase()
	}()

	// 备份 binlog
	wg.Add(1)
	go func() {
		defer wg.Done()
		mu.Lock()
		defer mu.Unlock()
		backupService.BackupBinlog()
	}()

	// 等待所有 goroutine 完成
	wg.Wait()

	log.Println("# 所有备份任务完成")
}

程序入口函数使用了goroutine、锁,主要为了确保备份脚本的多线程执行,提高备份速率,确保数据的准确性

8、Makefile解读

在Go语言的开发过程中,项目构建是一个关键环节,它涉及代码编译、打包、测试等多个步骤。 go build作为官方提供的命令行工具,提供了基本的构建功能,而Makefile则是一种更灵活的自动化构建脚本,适用于复杂项目的需求。

	以下makefile文件中定义了构建完成后程序的二进制名称、指定了构建所需要的go相关命令、程序的入口目录、三个不同阶段实现的不同功能
	build阶段
		等价于执行go build xx.go 命令
	clean阶段
		清除掉构建后的二进制程序文件及多余的文件
	run阶段
		构建完成后执行,等价于go run main.go
GOCMD=go
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOGET=$(GOCMD) get
BINARY_NAME=mysql_backup
CONFIG_FILE=configs/config.yaml

# Directories
CMD_DIR=cmd

all: build

build:  //
        $(GOBUILD) -o $(BINARY_NAME) $(CMD_DIR)/main.go
        rm -rf ./xtrabackup_backupfiles

clean: 
        $(GOCLEAN)
        rm -rf $(BINARY_NAME)
        rm -rf ./xtrabackup_backupfiles

run:  build
        ./$(BINARY_NAME) $(CONFIG_FILE)
        rm -rf ./xtrabackup_backupfiles

9、Scripts目录解读

主要是为了方便运维人员的可操作性,以及将输出到控制台的相关信息保存到对应的文件中,方便查看对应的日志文件,减少控制台不必要的输出
前提是linux服务器中必须要先安装gcc等相关命令,否则脚本会执行报错

#!/bin/bash

# 检查是否传递了参数
if [ "$#" -ne 1 ]; then
    echo "Usage: $0 {build|clean|run}"
    exit 1
fi
# 获取脚本所在的目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# 推断 Makefile 所在的目录(假设 Makefile 在脚本目录的上一级)
MAKEFILE_DIR="$(dirname "$SCRIPT_DIR")"

# nohup.out 文件路径
NOHUP_FILE="$SCRIPT_DIR/nohup.out"

# 检查 nohup.out 文件是否存在,如果不存在则创建
if [ ! -f "$NOHUP_FILE" ]; then
    touch "$NOHUP_FILE"
fi

# 根据参数执行相应的 make 命令
case "$1" in
    build)
        echo "===== Starting build at $(date) =====" >> "$NOHUP_FILE"
        make -C "$MAKEFILE_DIR"  build &>> $NOHUP_FILE
        echo "===== Finished build at $(date) =====" >> "$NOHUP_FILE"
        ;;
    clean)
        echo "===== Starting clean at $(date) =====" >> "$NOHUP_FILE"
        make -C "$MAKEFILE_DIR"  clean &>> $NOHUP_FILE
        echo "===== Finished clean at $(date) =====" >> "$NOHUP_FILE"
        ;;
    run)
        echo "===== Starting run at $(date) =====" >> "$NOHUP_FILE"
        make -C "$MAKEFILE_DIR"  run &>> $NOHUP_FILE
        echo "===== Finished run at $(date) =====" >> "$NOHUP_FILE"
        ;;
    *)
        echo "Invalid option: $1"
        echo "Usage: $0 {build|clean|run}"
        exit 1
        ;;
esac

10、程序执行步骤解读

	程序调用流程
		main.go
			-->Init.go
				-->config.yaml
					-->models.go
						-->utils.go
			-->models.go
				-->service.go
				
			-->launch.sh
			
	1、将整个脚本目录放置在对应的服务器目录下,例如: /data/目录下

	2、执行以下命令,前提是服务器已安装golnag环境,此处以golang 1.19环境为示例
		cd /data/xx/
		go mod init xxx目录名 #产生go,mod文件
		go mod tidy   #服务器将会下载程序中依赖的golang标准库

	3、执行scritps目录下的脚本文件
		cd /data/xx/scripts/
		./launch.sh build  #对golang程序进行构建
		./launch.sh run    #执行golang备份脚本程序
		
	4、查看scripts目录下的nohup.out文件,查看执行命令是否有报错

三、备份脚本执行示例

1、服务器准备

可以ping通公网的vmware服务器,并提前安装好mysql-5.7版本、golang1.18版本
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、上传golang备份脚本至服务器目录

在这里插入图片描述

3、执行命令下载标准库

go mod init xx
go mod tidy
在这里插入图片描述

4、执行scripts目录下的脚本

`cd /xx/xx/scripts/  && ./launch.sh build`

在这里插入图片描述

./launch.sh run

在这里插入图片描述

查看scripts/nohup.out日志文件,无报错即备份成功

在这里插入图片描述

查看备份数据保存的目录是否已存在新的备份

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
至此整个备份程序演示完成!成功的对数据库进行了全量备份及备份日志做出了保存


总结

以上就是我今天分享的数据库备份脚本–golang脚本,结合当前自学的golang知识,对shell脚本进行了改写,主要是加深对golang的语法的理解以及标准库的学习,虽然实现了对应的功能,但是自我感觉整个代码可读性较差,且代码冗余浮肿,大家看看即可!

  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值