diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000000000000000000000000000000000..7e257db92b8cb3908b1e94b71eef72df760d67dc --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": [] +} \ No newline at end of file diff --git a/Backup_module/Backup.go b/Backup_module/Backup.go index b78cc3832f97c5c14ce99ecee3d9b446889645dd..322d69041ec4704cbb838f94b45ecd228148742f 100644 --- a/Backup_module/Backup.go +++ b/Backup_module/Backup.go @@ -33,7 +33,7 @@ func Backup() { fmt.Println("备份数据库失败:", err) return } - fmt.Println("备份全库全表成功:") + color.BgGreen.Println("备份全库全表成功:") case 2: rows, err := Global.DB.Query("SHOW DATABASES") if err != nil { @@ -70,7 +70,7 @@ func Backup() { break } go backup(Appoint_dbName) - + time.Sleep(time.Second) } default: Completed.Printf("请重新输入") @@ -85,14 +85,27 @@ func backup(dbName string) { fileName := fmt.Sprintf("%s_%s.sql", dbName, time.Now().Format("20060102_150405")) // 备份命令 - cmd := exec.Command("mysqldump", "-u"+Global.User, "-p"+Global.Password, "--databases", dbName, "--result-file="+fileName) - // fmt.Println(cmd) - // 执行备份命令 - err := cmd.Run() - if err != nil { - fmt.Println("备份数据库失败:", err) - return + if Global.Password != "" { + cmd := exec.Command("mysqldump", "-u"+Global.User, "-p"+Global.Password, "--databases", dbName, "--result-file="+fileName) + // 执行备份命令 + err := cmd.Run() + if err != nil { + fmt.Println("备份数据库失败:", err) + return + } + + color.BgGreen.Println("备份数据库成功:", dbName) + + } else { + cmd := exec.Command("mysqldump", "-u"+Global.User, "--databases", dbName, "--result-file="+fileName) + // 执行备份命令 + err := cmd.Run() + if err != nil { + fmt.Println("备份数据库失败:", err) + return + } + + color.BgGreen.Println("备份数据库成功:", dbName) } - fmt.Println("备份数据库成功:", dbName) } diff --git a/Export_module/Export.go b/Export_module/Export.go new file mode 100644 index 0000000000000000000000000000000000000000..bc025b96702596c7ccb99139ff3ecc121aac439f --- /dev/null +++ b/Export_module/Export.go @@ -0,0 +1,282 @@ +package Export_module + +import ( + "bufio" + "dba_toolbox/Global" + "encoding/csv" + "fmt" + _ "io" + "os" + "path/filepath" + "time" + + "github.com/gookit/color" +) + +var ( + // color + Green = color.Green.Render + Red = color.Red.Render + Completed = color.S256(255, 27) +) + +func Export() { + + var table_name string + dirPath, err := getCurrentFilePath() + if err != nil { + panic(err) + } + fmt.Printf("当前文件所在路径:%s\n", dirPath) + + fmt.Println("欢迎使用DBA_TOOLBOX的数据导出功能") + fmt.Print("请输入要导出的表_格式[库名.表名]_例:test.tb_test> ") + fmt.Scanln(&table_name) + start_Time := time.Now() + timeStr := start_Time.Format("2006-01-02 15:04:05") + fmt.Println("开始时间为:", timeStr) + + //数据库保护查询地址 + // address := Get_address() + + var count int + query := fmt.Sprintf("SELECT COUNT(*) FROM %s", table_name) + err = Global.DB.QueryRow(query).Scan(&count) + if err != nil { + panic(err.Error()) + } + //切成4线程 + steps := count / 4 + //测试切成6线程 + // steps := count / 6 + sharding := 0 + for i := 0; i < 4; i++ { + + fileName := fmt.Sprintf("%s/%d.csv", dirPath, i) + // fmt.Print(fileName) + + sharding = steps * i + go CV_EXPORTS(sharding, steps, table_name, fileName) + time.Sleep(time.Second) + + } + // mergeCSV(dirPath) + color.BgGreen.Println("导出表成功:", table_name) + //获取结束时间 + end_Time := time.Now() + //转时间格式为字符串 + timeStre := end_Time.Format("2006-01-02 15:04:05") + //输出结束时间 + fmt.Println("结束时间为:", timeStre) + //计算两时间相差 + elapsed_time := end_Time.Sub(start_Time) + //输出总耗时 + fmt.Println("总耗时:", elapsed_time) +} + +func CV_EXPORTS(start int, steps int, table string, filename string) { + + query := fmt.Sprintf("select * into outfile '%s' from %s Limit %d , %d;", filename, table, start, steps) + fmt.Println(query) + counts, err := Global.DB.Query(query) + if err != nil { + fmt.Printf("单表导出失败err:%v\n", err) + } + defer counts.Close() + +} + +// func mergeCSV(dirPath string) { +// files := []string{"1.csv", "2.csv", "3.csv", "4.csv", "5.csv"} + +// outputFile, err := os.OpenFile(filepath.Join(dirPath, "0.csv"), os.O_APPEND|os.O_WRONLY, os.ModeAppend) +// if err != nil { +// panic(err) +// } +// defer outputFile.Close() + +// writer := csv.NewWriter(outputFile) +// defer writer.Flush() + +// for _, file := range files { +// inputFile, err := os.Open(filepath.Join(dirPath, file)) +// if err != nil { +// panic(err) +// } +// defer inputFile.Close() + +// reader := csv.NewReader(inputFile) +// for { +// record, err := reader.Read() +// if err != nil { +// if err == io.EOF { +// break +// } else { +// panic(err) +// } +// } +// if err := writer.Write(record); err != nil { +// panic(err) +// } +// } +// } +// } + +// 第二次版本,并行处理,但是会导致顺序不一样func mergeCSV(dirPath string) { +// files := []string{"1.csv", "2.csv", "3.csv"} + +// outputFile, err := os.OpenFile(filepath.Join(dirPath, "0.csv"), os.O_APPEND|os.O_WRONLY, os.ModeAppend) +// if err != nil { +// panic(err) +// } +// defer outputFile.Close() + +// writer := csv.NewWriter(outputFile) +// defer writer.Flush() + +// recordCh := make(chan []string) + +// var wg sync.WaitGroup +// for _, file := range files { +// wg.Add(1) +// go func(file string) { +// defer wg.Done() + +// inputFile, err := os.Open(filepath.Join(dirPath, file)) +// if err != nil { +// panic(err) +// } +// defer inputFile.Close() + +// reader := csv.NewReader(inputFile) +// for { +// record, err := reader.Read() +// if err != nil { +// if err == io.EOF { +// break +// } else { +// panic(err) +// } +// } +// recordCh <- record +// } +// }(file) +// } + +// go func() { +// wg.Wait() +// close(recordCh) +// }() + +// for record := range recordCh { +// if err := writer.Write(record); err != nil { +// panic(err) +// } +// } +// } + +// 第一次优化版本func mergeCSV(dirPath string) { +// files := []string{"0.csv", "1.csv", "2.csv", "3.csv"} + +// outputFile, err := os.Create("All_csv.csv") +// if err != nil { +// panic(err) +// } +// defer outputFile.Close() + +// writer := csv.NewWriter(outputFile) +// defer writer.Flush() + +// recordCh := make(chan []string) + +// var wg sync.WaitGroup +// for _, file := range files { +// wg.Add(1) +// go func(file string) { +// defer wg.Done() + +// inputFile, err := os.Open(filepath.Join(dirPath, file)) +// if err != nil { +// panic(err) +// } +// defer inputFile.Close() + +// reader := csv.NewReader(inputFile) +// for { +// record, err := reader.Read() +// if err != nil { +// if err == io.EOF { +// break +// } else { +// panic(err) +// } +// } +// recordCh <- record +// } +// }(file) +// } + +// go func() { +// wg.Wait() +// close(recordCh) +// }() + +// for record := range recordCh { +// if err := writer.Write(record); err != nil { +// panic(err) +// } +// } +// } + +// 最初版本 +func Merge_cvs(dirPath string) { + // 定义CSV文件路径 + + files := []string{"0.csv", "1.csv", "2.csv", "3.csv"} + + // 创建输出文件 + outputFile, err := os.Create("All_csv.csv") + if err != nil { + panic(err) + } + defer outputFile.Close() + + // 创建CSV writer + writer := csv.NewWriter(bufio.NewWriter(outputFile)) + defer writer.Flush() + + // 从多个文件中读取数据并写入输出文件 + for _, file := range files { + inputFile, err := os.Open(filepath.Join(dirPath, file)) + if err != nil { + panic(err) + } + defer inputFile.Close() + + // 创建CSV reader + reader := csv.NewReader(bufio.NewReader(inputFile)) + + // 从CSV文件中读取数据 + records, err := reader.ReadAll() + if err != nil { + panic(err) + } + + // 将数据写入输出文件 + for _, record := range records { + if err := writer.Write(record); err != nil { + panic(err) + } + } + } +} + +func getCurrentFilePath() (string, error) { + dirPath, err := os.Getwd() + if err != nil { + fmt.Println("无法获取当前工作目录:", err) + } + //winwos测试用 + // dirPath = "D:/mycode/gitee/dba_toolbox" + return dirPath, nil +} diff --git a/Menu/Menu.go b/Menu/Menu.go index ec67c8dd4c7e6f4ad1c9700ed1784ccb27d82c98..ae8ea076804f0b4f376092a63863470e03489239 100644 --- a/Menu/Menu.go +++ b/Menu/Menu.go @@ -2,6 +2,7 @@ package Menu import ( "dba_toolbox/Backup_module" + "dba_toolbox/Export_module" "dba_toolbox/Inspection_items/Index" "dba_toolbox/Inspection_items/Inspection_suggestions" "dba_toolbox/Inspection_items/Privileges" @@ -65,8 +66,10 @@ func Menu(m string) { Privileges.User_Privileges_Inspection() case "monitor": Monitor.DB_monitor() - case "Backup": + case "backup": Backup_module.Backup() + case "export": + Export_module.Export() default: fmt.Println("You have not selected a mode") } diff --git a/README.md b/README.md index bdaedb3084d379884afed684e90f806b1a6897bb..6687795de1ce88aafafde044d1fa37848dd786ef 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![](https://img.shields.io/badge/License-Apache_v2.0-blue.svg)](https://gitee.com/GreatSQL/GreatSQL/blob/master/LICENSE) [![](https://img.shields.io/badge/release-1.2.1-blue.svg)](https://gitee.com/GreatSQL/gt-checksum/releases/tag/1.2.1) ![输入图片说明](Menu/DBA_Toolbox_Logo.png) + # DBA工具箱 DBA_TOOLBOX ## 简介: 这款软件是一个全面的数据库管理工具,旨在帮助DBA更轻松地管理和维护数据库。它提供了各种功能,包括数据库监控、性能优化、备份和恢复、安全管理、查询分析等等。此外,它还支持多种数据库平台,如MySQL、Oracle、SQL Server等。无论您是初学者还是经验丰富的DBA,这个工具箱都将成为您工作中不可或缺的助手。 @@ -13,49 +14,83 @@ - [V1.2 Linux版本](https://gitee.com/KAITOO/db_-osinspection/releases/download/V1.2/DB_OSInspection) ## 快速上手 ```go -Windows环境下可直接使用,不能保存巡检结果 -./DBA_TOOLBOX -u 用户名 -p 密码 - -在Linux环境下保存巡检结果可以用 - ./DBA_TOOLBOX -u 用户名 -p 密码 >err.log - -监测模式 -./DBA_TOOLBOX -u 用户名 -p 密码 -m monitor +./DBA_TOOLBOX -u 用户名 -p 密码 -m 选择功能 ``` ### 参数解析 **所有输入后面都需要跟空格,否则会错误** -- -u 输入用户名 默认为root +- -u 输入用户名[默认为root] - -p 输入密码 -- -nw 输入链接模式 默认为tcp 一般不修改 -- -P 输入IP号和端口号 默认为localhost:3306 -- -m -模式默认选择all全部巡检、table只巡检表、index只巡检索引、variables只巡检重要参数、status只巡检重要状态、user只巡检用户、privileges只巡检权限、monitor实现数据库监控 +- -nw 输入链接模式[默认为 tcp 一般不修改] +- -P 输入IP号和端口号[默认为 localhost:3306] +- -m 模式默认选择 + - all数据库巡检 + - monitor实现数据库监控 + - backup数据库备份 + - export单表导出 - -v 输出版本号 - -h 输出帮助 ## 已实现功能 -### 实现数据库监控可实时输出 --m 选择monitor 模式即可时间监控通用日志,实现对数据库操作实时输出 -### 实现表巡检 -- 大小超过10G的表 \ 索引超过6个的表 \ 碎片率超过50%的表 -- 行数超过1000万行的表 \ 非默认字符集的表 \ 含有大字段的表 -- varchar定义超长的表 \ 无主键/索引的表 -### 索引巡检 -- 重复索引 \ 索引列超过5个的索引 \ 无用索引 -### 重要参数 -- version\innodb_buffer_pool_size\innodb_flush_log_at_trx_commit -- innodb_log_file_size\innodb_log_files_in_group\innodb_file_per_table -- innodb_max_dirty_pages_pct\sync_binlog\max_connections -- table_open_cache\table_definition_cache -### 重要状态指标 -- Uptime\Opened_files\Opened_table_definitions -- Opened_tables\Max_used_connections -- Threads_created\Threads_connected -- Aborted_connects\Aborted_clients\Table_locks_waited -- Innodb_buffer_pool_wait_free\Innodb_log_waits -- Table_locks_waited\Innodb_row_lock_waits -- Innodb_row_lock_time_avg\Binlog_cache_disk_use\Created_tmp_disk_tables -### 用户检查 -- 无密码用户 -- %用户 -### 权限检查 -- 根据检出来的用户检查权限 \ No newline at end of file +### Ⅰ、实现数据库监控可实时输出 + +```bash +-m moitot 模式即可时间监控通用日志,实现对数据库操作实时输出 +``` + +### Ⅱ、实现数据库深度巡检 + +```bash +# Windows环境下可直接使用,但不能保存巡检结果 +./DBA_TOOLBOX -u 用户名 -p 密码 + +# 在Linux环境下保存巡检结果可以用 + ./DBA_TOOLBOX -u 用户名 -p 密码 >err.log +``` + +#### 表巡检 +✅大小超过10G的表✅索引超过6个的表✅碎片率超过50%的表✅行数超过1000万行的表✅非默认字符集的表 + +✅含有大字段的表✅VarChar定义超长的表✅无主键✅索引的表 + +#### 索引巡检 +✅重复索引✅索引列超过5个的索引✅无用索引 + +#### 重要参数 +✅version✅innodb_buffer_pool_size✅innodb_flush_log_at_trx_commit + +✅innodb_log_file_size✅innodb_log_files_in_group✅innodb_file_per_table + +✅innodb_max_dirty_pages_pct✅sync_binlog✅max_connections + +✅table_open_cache✅table_definition_cache + +#### 重要状态指标 +✅Uptime✅Opened_files✅Opened_table_definitions✅Opened_tables✅Max_used_connections + +✅Threads_created✅Threads_connected✅Aborted_connects✅Aborted_clients✅Table_locks_waited + +✅Innodb_buffer_pool_wait_free✅Innodb_log_waits✅Table_locks_waited✅Innodb_row_lock_waits + +✅Innodb_row_lock_time_avg✅Binlog_cache_disk_use✅Created_tmp_disk_tables + +#### 用户检查 +✅无密码用户✅Host为%用户 + +#### 权限检查 +✅根据检出来的用户检查权限 + +### Ⅲ、实现数据库备份 + +```bash +-m backup 即可实现对数据库进行备份。 +分为三种模式:[1,全库全表备份]、[2,除去系统表备份]、[3,指定库备份] +*备份采用多线程备份,备份速度更快 +``` + +### Ⅳ、实现表导出功能 + +```bash +-m export 即可实现对单表导出为csv格式 +*导出采用多线程速度更快 +``` +>注意!此功能需MySQL所在文件有权限,建议放到MySQL数据目录下 + diff --git a/main b/main new file mode 100644 index 0000000000000000000000000000000000000000..ff3bce5e919787e809210b414fb2e64f6ae99fab Binary files /dev/null and b/main differ diff --git a/main.exe b/main.exe new file mode 100644 index 0000000000000000000000000000000000000000..55038a4171fe446af732803432438dbcf5ba2c76 Binary files /dev/null and b/main.exe differ diff --git a/main.go b/main.go index b1e849f3897e0ca6b7dc608d26c5cbf4495139e7..2bdf5d66d135165e0ce93e99c809ede925c57296 100644 --- a/main.go +++ b/main.go @@ -55,12 +55,13 @@ func main() { u := flag.String("u", "root", "input username") //获取密码 p := flag.String("p", "GreatSQL@2022", "input password") + // p := flag.String("p", " ", "input password") //获取链接模式 nw := flag.String("nw", "tcp", "input netWork") //获取地址和端口号 P := flag.String("P", "localhost:3306", "input port") /*选择模式:all全部巡检、table只巡检表、index只巡检索引、variables只巡检重要参数、 - status只巡检重要状态、user只巡检用户、privileges只巡检权限、monitor新增功能*/ + status只巡检重要状态、user只巡检用户、privileges只巡检权限、monitor新增功能、Backup备份功能*/ m := flag.String("m", "all", "input model") //版本号 v := flag.Bool("v", false, "input port")