背景

navicat是mysql可视化工具中最棒的,但是,在处理视图的导入导出方面,它是按照视图名称的字母顺序来处理的,若视图存在依赖,在导入过程中就会报错。前面已经用python写了一个,但在使用过程中,遇到xfffd编码,python的pymysql会直接崩溃。发现golang没有这个问题,正好用go重写,来熟悉golang。

一些关键点

map & json,在处理主键与外键信息时,需要用到json数据结构来存储中间结果,因为要灵活处理,在golang中只能用map[string]interface{}来处理。

interface{} 相当于java中的object,能接受任意数据类型,方便但在使用时要做到心中有数,不然一旦数据类型不匹配,程序就会崩溃。

xfffd ,utf8中的占位符,超出范围的utf8mb4入库后,会被存储为xfffd,数据导出时,需要过滤掉。

goroutine, golang的并发支持很独特,我们的工具支持多个库同时备份,很容易使用goroutine来实现并行。

代码解析

按功能模块对核心代码进行说明

main.go,并发、命令行参数

使用命令行参数,接受一个参数,来指定备份的内容

package common

type OpFlag struct {

Tables bool //表结构

Datum bool //表结构和数据

Views bool //视图

Funcs bool //函数与存储过程

}

main.go,程序入口,处理命令行参数

if len(os.Args) > 1 {

flag = common.OpFlag{

Tables: false,

Datum: false,

Views: false,

Funcs: false,

}

switch os.Args[1] { //接受一个参数

case "table":

flag.Tables = true //根据参数设定标识量

case "data":

flag.Tables = true

flag.Datum = true

case "views":

flag.Views = true

case "funcs":

flag.Funcs = true

default: //参数不正确,报错退出

log.Fatal("You arg must be in : table, data, views or funcs.")

}

}else{ //无参数,默认导出所有

flag = common.OpFlag{

Tables: true,

Datum: true,

Views: true,

Funcs: true,

}

}

err := backUp.Export(flag) 根据参数进行数据库备份

Export.go

备份主流程,根据configs.json生成goroutine来备份数据库,并等待完成。

var configs interface{}

fr, err := os.Open("./configs.json")

if err != nil {

return err

}

decoder := json.NewDecoder(fr) //解析配置文件

err = decoder.Decode(&configs)

confs := configs.(map[string]interface{})

workDir := confs["workDir"].(string)

ch := make(chan string) //通道变量

for key, value := range confs {

if strings.HasPrefix(key, "db_") {

dbConf := value.(map[string]interface{})

dbConn := common.DbConnFields{ //数据库相应配置

DbHost: dbConf["db_host"].(string),

DbPort: int(dbConf["db_port"].(float64)),

DbUser: dbConf["db_user"].(string),

DbPass: dbConf["db_pass"].(string),

DbName: dbConf["db_name"].(string),

DbCharset: dbConf["db_charset"].(string),

}

if dbConf["file_alias"] != nil { //生成sql备份文件的命名

dbConn.FileAlias = dbConf["file_alias"].(string)

}

go ExportOne(dbConn, workDir, ch, flag) //创建协程

}

}

for key := range confs { //阻塞主进程,待所有协程完成工作

if strings.HasPrefix(key, "db_") {

fmt.Print(

}

}

return nil

你需要编写如下的配置文件来描述你要备份的数据库:

{

"db_name1": {

"db_host": "192.168.1.8",

"db_port": 3306,

"db_user": "root",

"db_pass": "123456",

"db_name": "name1",

"db_charset": "utf8mb4",

"file_alias": "file name1"

},

"db_name2": {

"db_host": "localhost",

"db_port": 3306,

"db_user": "root",

"db_pass": "123456",

"db_name": "name2",

"db_charset": "utf8mb4"

},

"database_dialect": "mysql",

"workDir": "/home/zhoutk/gocodes/goTools/"

}

ExportOne.go

备份一个数据库

fileName := fields.FileAlias

setSqlHeader(fields, fileName) //设置导出文件说明

if flag.Tables { //如果表设置为真,导出表结构

err := exportTables(fileName, fields, flag) //具体算法请参照源代码

if err != nil {

ch

return

}

}

if flag.Views { //如果视图设置为真,导出视图

err := exportViews(fileName, fields)//具体算法请参照源代码,或python算法

if err != nil {

ch

return

}

}

if flag.Funcs { //如果函数设置为真,导出函数和存储过程

err := exportFuncs(fileName, fields)//具体算法请参照源代码

if err != nil {

ch

return

}

}

//导出工作完成,向通道输入信息

ch

MysqlDao.go

数据库查询通用封装,此工具只使用了ExecuteWithDbConn。灵活的使用map与interface{},将结果转化为键值对象返回。

func ExecuteWithDbConn(sql string, values []interface{}, fields common.DbConnFields) (map[string]interface{}, error) {

rs := make(map[string]interface{})

dao, err := mysql.Open("mysql", fields.DbUser + ":"+fields.DbPass+"@tcp("+fields.DbHost+":"+

strconv.Itoa(fields.DbPort)+")/"+fields.DbName+"?charset="+fields.DbCharset)

defer dao.Close()

if err != nil {

rs["code"] = 204

return rs, err

}

stmt, err := dao.Prepare(sql)

if err != nil {

rs["code"] = 204

return rs, err

}

rows, err := stmt.Query(values...)

if err != nil {

rs["code"] = 204

return rs, err

}

columns, err := rows.Columns() //取出字段名称

vs := make([]mysql.RawBytes, len(columns))

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

for i := range vs { //预设取值地址

scans[i] = &vs[i]

}

var result []map[string]interface{}

for rows.Next() {

_ = rows.Scan(scans...) //塡入一列值

each := make(map[string]interface{})

for i, col := range vs {

if col != nil {

each[columns[i]] = FilterHolder(string(col)) //过滤/xfffd

}else{

each[columns[i]] = nil

}

}

result = append(result, each)

}

rs["code"] = 200

//data, _ := json.Marshal(result)

rs["rows"] = result

return rs, err

}

项目地址

使用方法

git clone https://github.com/zhoutk/goTools

cd goTools

go get

go run main.go

go buid main.go

./main #export all things of database

./main table #export tables

./main data #export tables & data

./main views #export views

./main funcs #export funcs & stored procedures

总结

以上所述是小编给大家介绍的golang实现mysql数据库备份的操作方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!