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
}
具体的函数如上所示,大家可以根据自己的需求来添加定时等相关需求
1468






