Operation Method of golang Realizing mysql Database Backup
- 2021-10-27 09:35:07
- OfStack
Background
navicat is the best of the mysql visualizers, but when it comes to importing and exporting views, it is done in alphabetical order of view names, and errors are reported during importing if the views have dependencies. I have written one with python before, but when I encounter xfffd coding, pymysql of python will crash directly. I found that golang didn't have this problem, so I just rewrote it with go to get familiar with golang.
1 Some key points
map & In json, json data structure is needed to store intermediate results when processing primary key and foreign key information. Because of flexible processing, map [string] interface {} can only be used in golang. interface {} is equivalent to object in java, which can accept any data type, but it is convenient to know fairly well when using it, otherwise, the program will crash if the data types do not match. The placeholders in xfffd, utf8 and utf8mb4 out of range will be stored as xfffd after being put into storage, and need to be filtered out when exporting data. The concurrency support of goroutine and golang is unique. Our tool supports simultaneous backup of multiple libraries, so it is easy to use goroutine to realize concurrency.Code parsing
Explain the core code by functional module
main. go, concurrent, command line arguments
Use command line arguments and accept 1 parameter to specify the contents of the backup
package common
type OpFlag struct {
Tables bool // Table structure
Datum bool // Table structure and data
Views bool // View
Funcs bool // Functions and stored procedures
}
main. go, program entry, processing command line parameters
if len(os.Args) > 1 {
flag = common.OpFlag{
Tables: false,
Datum: false,
Views: false,
Funcs: false,
}
switch os.Args[1] { // Accept 1 Parameters
case "table":
flag.Tables = true // Set the identification amount according to the parameters
case "data":
flag.Tables = true
flag.Datum = true
case "views":
flag.Views = true
case "funcs":
flag.Funcs = true
default: // Incorrect parameters, report error and exit
log.Fatal("You arg must be in : table, data, views or funcs.")
}
}else{ // No parameters, export all by default
flag = common.OpFlag{
Tables: true,
Datum: true,
Views: true,
Funcs: true,
}
}
err := backUp.Export(flag) Backup database according to parameters
Export.go
Backup main flow, according to configs. json to generate goroutine backup database, and wait for completion.
var configs interface{}
fr, err := os.Open("./configs.json")
if err != nil {
return err
}
decoder := json.NewDecoder(fr) // Parsing Configuration File
err = decoder.Decode(&configs)
confs := configs.(map[string]interface{})
workDir := confs["workDir"].(string)
ch := make(chan string) // Channel variable
for key, value := range confs {
if strings.HasPrefix(key, "db_") {
dbConf := value.(map[string]interface{})
dbConn := common.DbConnFields{ // Database corresponding configuration
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 { // Generate sql Naming of backup files
dbConn.FileAlias = dbConf["file_alias"].(string)
}
go ExportOne(dbConn, workDir, ch, flag) // Create a cooperative process
}
}
for key := range confs { // Block the main process until all the co-processes finish their work
if strings.HasPrefix(key, "db_") {
fmt.Print( <- ch )
}
}
return nil
You need to write the following configuration file to describe the database you want to back up:
{
"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
Back up 1 database
fileName : = fields.FileAlias
setSqlHeader(fields, fileName) // Set export file description
if flag.Tables { // Export table structure if table is set to true
err := exportTables(fileName, fields, flag) // Please refer to the source code for specific algorithms
if err != nil {
ch <- fmt.Sprintln("Error: ", fields.DbName, "\t export tables throw, \t", err)
return
}
}
if flag.Views { // If the view is set to true, export the view
err := exportViews(fileName, fields)// Please refer to the source code for specific algorithms, or python Algorithm
if err != nil {
ch <- fmt.Sprintln("Error: ", fields.DbName, "\t export views throw, \t", err)
return
}
}
if flag.Funcs { // If the function is set to true, export the function and stored procedure
err := exportFuncs(fileName, fields)// Please refer to the source code for specific algorithms
if err != nil {
ch <- fmt.Sprintln("Error: ", fields.DbName, "\t export funcs throw, \t", err)
return
}
}
// Export is complete, enter information into the channel
ch <- fmt.Sprintln("Export ", fields.DbName, "\t success at \t", time.Now().Format("2006-01-02 15:04:05"))
MysqlDao.go
General encapsulation of database query, this tool only uses ExecuteWithDbConn. Flexible use of map and interface {}, the results are converted into key-value objects returned.
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() // Take out the field name
vs := make([]mysql.RawBytes, len(columns))
scans := make([]interface{}, len(columns))
for i := range vs { // Preset value address
scans[i] = &vs[i]
}
var result []map[string]interface{}
for rows.Next() {
_ = rows.Scan(scans...) // Into 1 Column value
each := make(map[string]interface{})
for i, col := range vs {
if col != nil {
each[columns[i]] = FilterHolder(string(col)) // Filter /xfffd
}else{
each[columns[i]] = nil
}
}
result = append(result, each)
}
rs["code"] = 200
//data, _ := json.Marshal(result)
rs["rows"] = result
return rs, err
}
Project address
https://github.com/zhoutk/goTools
Usage
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
Summarize