Go语言编写的MySQL数据导出备份脚本

先通过数据表结构同步功能将源数据库和目标数据库的结构同步。

从config/config.json获取源数据库配置信息、目的数据库配置信息,创建fromDSN和toDsn,打开数据库之后获取到fromDb和toDb用于后续的SQL执行。

其中config.json格式为:

{
    "sqlType":"mysql",
    "fromDb" : {
        "host":"localhost",
        "port":3306,
        "userName":"fromdb-username",
        "password":"fromdb-pwd",
        "database":"fromdb-database-name",
        "charset":"utf8"
    },
    "toDb":{
        "host":"localhost",
        "port":3306,
        "userName":"todb-username",
        "password":"todb-pwd",
        "database":"todb-database-name",
        "charset":"utf8"
    }
}

在程序所在目录下创建success.txt和tableList.txt文档,success.txt中保存导出成功的表名,tableList.txt中自行填入所要导出的数据表名,每个表名占据一行。

使用make([][]byte, len(cols))方式获取原来的表各个字段名称,再创建一个scans变量用于读取每个字段对应的内容,动态匹配每个表,不用每张表都创建一个结构体。

vals := make([][]byte, len(cols))
scans := make([]interface{}, len(cols))

这样做的优势是一旦你所要导出的表较多的时候,能够非常省心,否则维护每个表的struct都将会成为你的负担,尤其是当一些表的字段有增删的时候。

下面是完整代码。如有疑问请留言咨询,我会一一解答。

package main

import (
	"bufio"
	"database/sql"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"

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

var cfg Config
var fromDb *sql.DB
var toDb *sql.DB

func readConfig() {
	bts, err := ioutil.ReadFile("./config/config.json")
	if err != nil {
		log.Panicln("ReadFile error:", err)
	}
	err = json.Unmarshal(bts, &cfg)
	if err != nil {
		log.Panicln("Unmarshal config file error:", err)
	}
}

func connectSQL() {
	fromDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", cfg.FromDb.UserName, cfg.FromDb.Password,
		cfg.FromDb.Host, cfg.FromDb.Port, cfg.FromDb.Database, cfg.FromDb.Charset)

	fDb, err := sql.Open(cfg.SqlType, fromDSN)
	if err != nil {
		log.Panicln("sql Open error:", err)
	}

	toDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", cfg.ToDb.UserName, cfg.ToDb.Password,
		cfg.ToDb.Host, cfg.ToDb.Port, cfg.ToDb.Database, cfg.ToDb.Charset)

	tDb, err := sql.Open(cfg.SqlType, toDSN)
	if err != nil {
		fDb.Close()
		log.Panicln("sql Open error:", err)
	}

	fromDb = fDb
	toDb = tDb
}

func getTables() []string {
	sf, err := os.Open("./success.txt")
	if err != nil {
		log.Panicln("Open success file error:", err)
	}
	defer sf.Close()

	tableMap := make(map[string]bool)
	scanner1 := bufio.NewScanner(sf)
	for scanner1.Scan() {
		line := scanner1.Text()
		if len(line) < 1 {
			continue
		}
		tableMap[line] = true
	}

	f, err := os.Open("./tableList.txt")
	if err != nil {
		log.Panicln("Open table list error:", err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	var tables []string
	for scanner.Scan() {
		line := scanner.Text()
		if len(line) < 1 {
			continue
		}
		if tableMap[line] {
			continue
		}

		tables = append(tables, line)
	}
	return tables
}

func importData(table string) {
	querySql := "select * from " + table + ";"
	stmt, err := fromDb.Prepare(querySql)
	if err != nil {
		log.Panicln("import data from db Prepare error:", err)
	}

	rows, err := stmt.Query()
	if err != nil {
		log.Panicln("import data from db Query error:", err)
	}
	cols, err := rows.Columns()
	if err != nil {
		log.Panicln("import data from db Columns error:", err)
	}
	vals := make([][]byte, len(cols))
	scans := make([]interface{}, len(cols))
	for i := range vals {
		scans[i] = &vals[i]
	}

	//先删除再插入
	truncateSql := "truncate " + table + ";"
	//先清空表再插入
	truncateStmt, err := toDb.Prepare(truncateSql)
	if err != nil {
		log.Panicln("import data to db Prepare truncate error:", err)
	}
	_, err = truncateStmt.Exec()
	if err != nil {
		log.Panicln("import data to db Exec truncate sql error:", err)
	}

	//根据字段数量拼接insert语句
	insertSql := "insert into " + table + " ("
	insertTail := ""
	for i := range cols {
		//使用``将字段括起来防止出现表的字段和关键字一样时报1064 sql错误
		insertSql += "`" + cols[i] + "`"
		insertTail += "?"
		if i+1 == len(cols) {
			break
		}
		insertSql += ","
		insertTail += ","
	}
	insertSql += ") values (" + insertTail + ");"

	insertStmt, err := toDb.Prepare(insertSql)
	if err != nil {
		log.Panicln("import data to db Prepare error:", err)
	}

	for rows.Next() {
		//动态获取字段信息
		err = rows.Scan(scans...)
		if err != nil {
			log.Panicln("import data from db Scan error:", err)
		}
		//动态插入数据
		_, err = insertStmt.Exec(scans...)
		if err != nil {
			log.Panicln("import data to db Exec error:", err)
		}
	}
}

func main() {
	readConfig()

	connectSQL()
	defer fromDb.Close()
	defer toDb.Close()

	tables := getTables()

	f, err := os.OpenFile("./success.txt", os.O_APPEND, 0666)
	if err != nil {
		log.Panicln("Open success file error:", err)
	}
	defer f.Close()

	for index := range tables {
		log.Println("import data to table:", tables[index])
		importData(tables[index])
		log.Println("import data to table:", tables[index], " success")

		f.WriteString(tables[index] + "\r\n")
	}
}

你可能感兴趣的:(go,开源,mysql,golang,数据库)