通过go语言来实现对mysql数据库的备份

该文章已生成可运行项目,

1.应用场景

        当我们在项目中需要对数据库进行备份的时候,可以通过对数据查询的方式将数据备份下来,并实现后续的重新导入数据库的功能。适用于在项目中无法使用原生的数据库备份方法的情况下。以下代码由go语言编写

2.具体代码

       

import (
	"database/sql"
	"db_backup/global"
	"fmt"
	"os"
	"strings"
	"time"
)

// MySQLBackup 函数用于备份指定的 MySQL 数据库。
func MySQLBackup(user, password, dbName, dbHost, dbPort string) {
	// 记录备份开始的日志
	global.Logger.Info("Starting backup " + dbName + "...")
	
	// 设置数据库连接信息
	dbUser := user
	dbPassword := password

	// 构建备份目录路径
	backupDir := dbHost + "_" + dbName
	if err := ensureDirExists(backupDir); err != nil {
		// 如果创建目录失败,记录错误并返回
		global.Logger.Errorf("Failed to create directory: %v", err)
		return
	}

	// 构建备份文件路径
	backupFile := backupDir + string(os.PathSeparator) + time.Now().Format("2006-01-02_15-04-05") + ".sql"
	// 创建备份文件
	file, err := os.Create(backupFile)
	if err != nil {
		// 如果创建文件失败,记录错误并返回
		global.Logger.Errorf("Failed to create file: %v", err)
		return
	}
	defer file.Close() // 确保文件在函数结束时关闭

	// 写入文件头部信息
	_, err = file.WriteString(fmt.Sprintf(`
/*
 Navicat Premium Data Transfer

 Source Server         : %s
 Source Server Type    : MySQL
 Source Server Version : 50568
 Source Host           : %s:%s
 Source Schema         : %s

 Target Server Type    : MySQL
 Target Server Version : 50568
 File Encoding         : 65001

 Date: %s
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
`, dbHost, dbHost, dbPort, dbName, time.Now().Format("02/01/2006 15:04:05")))
	if err != nil {
		// 如果写入头部信息失败,记录错误并返回
		global.Logger.Errorf("Failed to write header: %v", err)
		return
	}

	// 连接数据库
	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPassword, dbHost, dbPort, dbName))
	if err != nil {
		// 如果连接数据库失败,记录错误并返回
		global.Logger.Errorf("Failed to connect to database: %v", err)
		return
	}
	defer db.Close() // 确保数据库连接在函数结束时关闭

	// 获取所有表名
	rows, err := db.Query(fmt.Sprintf(`
		SELECT TABLE_NAME
		FROM INFORMATION_SCHEMA.TABLES
		WHERE TABLE_SCHEMA = '%s'
		AND TABLE_TYPE = 'BASE TABLE'
	`, dbName))
	if err != nil {
		// 如果获取表名失败,记录错误并返回
		global.Logger.Errorf("Failed to get tables: %v", err)
		return
	}

	// 存储表名
	tables := []string{}
	for rows.Next() {
		var table string
		if err := rows.Scan(&table); err != nil {
			// 如果扫描表名失败,记录错误并返回
			global.Logger.Errorf("Failed to scan table: %v", err)
			return
		}
		tables = append(tables, table)
	}
	rows.Close() // 确保查询结果集在循环结束后关闭

	// 导出每个表的数据
	for _, table := range tables {
		// 重新建立数据库连接
		db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPassword, dbHost, dbPort, dbName))
		if err != nil {
			// 如果连接数据库失败,记录错误并返回
			global.Logger.Errorf("Failed to connect to database: %v", err)
			return
		}

		// 导出表结构
		structure, err := db.Query(fmt.Sprintf("SHOW CREATE TABLE `%s`", table))
		if err != nil {
			// 如果获取表结构失败,记录错误并返回
			global.Logger.Errorf("Failed to get table structure: %v", err)
			return
		}

		if structure.Next() {
			var tableName, createTable string
			columns, _ := structure.Columns()
			dest := make([]interface{}, len(columns))
			for i := range columns {
				dest[i] = new(sql.RawBytes)
			}

			if err := structure.Scan(dest...); err != nil {
				// 如果扫描表结构失败,记录错误并返回
				global.Logger.Errorf("Failed to scan table structure: %v", err)
				return
			}

			for i, col := range columns {
				if col == "Table" {
					tableName = string(*dest[i].(*sql.RawBytes))
				} else if col == "Create Table" {
					createTable = string(*dest[i].(*sql.RawBytes))
				}
			}

			// 写入表结构
			_, err := file.WriteString(fmt.Sprintf(`

-- ----------------------------
-- Table structure for %s
-- ----------------------------
DROP TABLE IF EXISTS %s;
%s;`, tableName, tableName, createTable))
			if err != nil {
				// 如果写入表结构失败,记录错误并返回
				global.Logger.Errorf("Failed to write table structure: %v", err)
				return
			}
			global.Logger.Info(tableName) // 记录已导出的表名
		}
		structure.Close() // 确保查询结果集在循环结束后关闭

		// 导出表数据
		data, err := db.Query(fmt.Sprintf("SELECT * FROM `%s`", table))
		if err != nil {
			// 如果获取表数据失败,记录错误并返回
			global.Logger.Errorf("Failed to get table data: %v", err)
			return
		}

		columns, err := data.Columns()
		if err != nil {
			// 如果获取列名失败,记录错误并返回
			global.Logger.Errorf("Failed to get columns: %v", err)
			return
		}

		values := make([]interface{}, len(columns))
		valuePtrs := make([]interface{}, len(columns))

		for i := range columns {
			valuePtrs[i] = &values[i]
		}

		// 写入表数据
		_, err = file.WriteString(fmt.Sprintf(`

-- ----------------------------
-- Records of %s
-- ----------------------------
`, table))
		if err != nil {
			// 如果写入表数据头部信息失败,记录错误并返回
			global.Logger.Errorf("Failed to write table data header: %v", err)
			return
		}

		for data.Next() {
			if err := data.Scan(valuePtrs...); err != nil {
				// 如果扫描行数据失败,记录错误并返回
				global.Logger.Errorf("Failed to scan row: %v", err)
				return
			}

			insertStmt := fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s);\n", table, joinColumns(columns), joinValues(values))
			_, err := file.WriteString(insertStmt)
			if err != nil {
				// 如果写入行数据失败,记录错误并返回
				global.Logger.Errorf("Failed to write row: %v", err)
				return
			}
		}
		data.Close() // 确保查询结果集在循环结束后关闭
		db.Close()   // 确保数据库连接在循环结束后关闭
	}

	// 写入文件尾部信息
	_, err = file.WriteString(`
SET FOREIGN_KEY_CHECKS = 1;
`)
	if err != nil {
		// 如果写入尾部信息失败,记录错误并返回
		global.Logger.Errorf("Failed to write footer: %v", err)
		return
	}

	// 记录备份完成的日志
	global.Logger.Info("Backup completed successfully!!!!!")
}

// joinColumns 将列名列表转换为 SQL 语句中的列名字符串
func joinColumns(columns []string) string {
	return fmt.Sprintf("`%s`", strings.Join(columns, "`, `"))
}

// joinValues 将值列表转换为 SQL 语句中的值字符串
func joinValues(values []interface{}) string {
	strValues := make([]string, len(values))
	for i, v := range values {
		switch v := v.(type) {
		case string:
			strValues[i] = fmt.Sprintf("'%s'", escapeString(v))
		case []byte:
			strValues[i] = fmt.Sprintf("'%s'", escapeString(string(v)))
		case time.Time:
			strValues[i] = fmt.Sprintf("'%s'", v.Format("2006-01-02 15:04:05"))
		case nil:
			strValues[i] = "NULL"
		default:
			strValues[i] = fmt.Sprintf("%v", v)
		}
	}
	return fmt.Sprintf("%s", strings.Join(strValues, ", "))
}

// escapeString 对字符串进行转义处理,防止 SQL 注入
func escapeString(s string) string {
	return strings.ReplaceAll(strings.ReplaceAll(s, `\`, `\\`), `'`, `\'`)
}

// ensureDirExists 确保目录存在,如果不存在则创建
func ensureDirExists(dirPath string) error {
	// 使用 Stat 函数检查目录是否存在
	_, err := os.Stat(dirPath)
	if os.IsNotExist(err) {
		// 目录不存在,创建目录
		if err := os.MkdirAll(dirPath, 0755); err != nil {
			return err
		}
	} else if err != nil {
		// 其他错误
		return err
	}
	return nil
}

        以上代码是将该数据库中所有的数据表进行备份,如果需要对特定的表进行备份可以用以下代码。

import (
	"database/sql"
	"db_backup/global"
	"fmt"
	"os"
	"strings"
	"time"
)

// MySQLBackup 函数用于备份指定的 MySQL 数据库中的指定表。
func MySQLBackup(user, password, dbName, dbHost, dbPort string, tablesToBackup []string) {
	global.Logger.Info("Starting backup " + dbName + "...")
	// 数据库连接信息
	dbUser := user
	dbPassword := password

	backupDir := dbHost + "_" + dbName
	if err := ensureDirExists(backupDir); err != nil {
		global.Logger.Errorf("Failed to create directory: %v", err)
		return
	}

	backupFile := backupDir + string(os.PathSeparator) + time.Now().Format("2006-01-02_15-04-05") + ".sql"
	// 创建备份文件
	file, err := os.Create(backupFile)
	if err != nil {
		global.Logger.Errorf("Failed to create file: %v", err)
		return
	}
	defer file.Close()

	// 写入文件头部信息
	_, err = file.WriteString(fmt.Sprintf(`
/*
 Navicat Premium Data Transfer

 Source Server         : %s
 Source Server Type    : MySQL
 Source Server Version : 50568
 Source Host           : %s:%s
 Source Schema         : %s

 Target Server Type    : MySQL
 Target Server Version : 50568
 File Encoding         : 65001

 Date: %s
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
`, dbHost, dbHost, dbPort, dbName, time.Now().Format("02/01/2006 15:04:05")))
	if err != nil {
		global.Logger.Errorf("Failed to write header: %v", err)
		return
	}

	// 连接数据库
	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPassword, dbHost, dbPort, dbName))
	if err != nil {
		global.Logger.Errorf("Failed to connect to database: %v", err)
		return
	}
	defer db.Close()

	// 导出指定的表的数据
	for _, table := range tablesToBackup {
		// 重新建立数据库连接
		db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPassword, dbHost, dbPort, dbName))
		if err != nil {
			global.Logger.Errorf("Failed to connect to database: %v", err)
			return
		}

		// 导出表结构
		structure, err := db.Query(fmt.Sprintf("SHOW CREATE TABLE `%s`", table))
		if err != nil {
			global.Logger.Errorf("Failed to get table structure: %v", err)
			return
		}

		if structure.Next() {
			var tableName, createTable string
			columns, _ := structure.Columns()
			dest := make([]interface{}, len(columns))
			for i := range columns {
				dest[i] = new(sql.RawBytes)
			}

			if err := structure.Scan(dest...); err != nil {
				global.Logger.Errorf("Failed to scan table structure: %v", err)
				return
			}

			for i, col := range columns {
				if col == "Table" {
					tableName = string(*dest[i].(*sql.RawBytes))
				} else if col == "Create Table" {
					createTable = string(*dest[i].(*sql.RawBytes))
				}
			}

			// 写入表结构
			_, err := file.WriteString(fmt.Sprintf(`

-- ----------------------------
-- Table structure for %s
-- ----------------------------
DROP TABLE IF EXISTS %s;
%s;`, tableName, tableName, createTable))
			if err != nil {
				global.Logger.Errorf("Failed to write table structure: %v", err)
				return
			}
			global.Logger.Info(tableName)
		}
		structure.Close()

		// 导出表数据
		data, err := db.Query(fmt.Sprintf("SELECT * FROM `%s`", table))
		if err != nil {
			global.Logger.Errorf("Failed to get table data: %v", err)
			return
		}

		columns, err := data.Columns()
		if err != nil {
			global.Logger.Errorf("Failed to get columns: %v", err)
			return
		}

		values := make([]interface{}, len(columns))
		valuePtrs := make([]interface{}, len(columns))

		for i := range columns {
			valuePtrs[i] = &values[i]
		}

		// 写入表数据
		_, err = file.WriteString(fmt.Sprintf(`

-- ----------------------------
-- Records of %s
-- ----------------------------
`, table))
		if err != nil {
			global.Logger.Errorf("Failed to write table data header: %v", err)
			return
		}

		for data.Next() {
			if err := data.Scan(valuePtrs...); err != nil {
				global.Logger.Errorf("Failed to scan row: %v", err)
				return
			}

			insertStmt := fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s);\n", table, joinColumns(columns), joinValues(values))
			_, err := file.WriteString(insertStmt)
			if err != nil {
				global.Logger.Errorf("Failed to write row: %v", err)
				return
			}
		}
		data.Close()
		db.Close()
	}

	// 写入文件尾部信息
	_, err = file.WriteString(`
SET FOREIGN_KEY_CHECKS = 1;
`)
	if err != nil {
		global.Logger.Errorf("Failed to write footer: %v", err)
		return
	}

	global.Logger.Info("Backup completed successfully!!!!!")
}

// joinColumns 将列名列表转换为 SQL 语句中的列名字符串
func joinColumns(columns []string) string {
	return fmt.Sprintf("`%s`", strings.Join(columns, "`, `"))
}

// joinValues 将值列表转换为 SQL 语句中的值字符串
func joinValues(values []interface{}) string {
	strValues := make([]string, len(values))
	for i, v := range values {
		switch v := v.(type) {
		case string:
			strValues[i] = fmt.Sprintf("'%s'", escapeString(v))
		case []byte:
			strValues[i] = fmt.Sprintf("'%s'", escapeString(string(v)))
		case time.Time:
			strValues[i] = fmt.Sprintf("'%s'", v.Format("2006-01-02 15:04:05"))
		case nil:
			strValues[i] = "NULL"
		default:
			strValues[i] = fmt.Sprintf("%v", v)
		}
	}
	return fmt.Sprintf("%s", strings.Join(strValues, ", "))
}

// escapeString 对字符串进行转义处理,防止 SQL 注入
func escapeString(s string) string {
	return strings.ReplaceAll(strings.ReplaceAll(s, `\`, `\\`), `'`, `\'`)
}

// ensureDirExists 确保目录存在,如果不存在则创建
func ensureDirExists(dirPath string) error {
	// 使用 Stat 函数检查目录是否存在
	_, err := os.Stat(dirPath)
	if os.IsNotExist(err) {
		// 目录不存在,创建目录
		if err := os.MkdirAll(dirPath, 0755); err != nil {
			return err
		}
	} else if err != nil {
		// 其他错误
		return err
	}
	return nil
}

        具体的函数如上所示,大家可以根据自己的需求来添加定时等相关需求

本文章已经生成可运行项目
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值