使用go備份StarRocks建表語(yǔ)句方法實(shí)例
Golang從StarRocks集群中導(dǎo)出所有數(shù)據(jù)庫(kù)的表結(jié)構(gòu)
使用StarRocks
的非穩(wěn)定版的最新版本,為了防止因?yàn)槲粗狟UG造成集群無(wú)法恢復(fù),或者為了定期備份生產(chǎn)集群的元數(shù)據(jù)信息。都需要一個(gè)能夠從集群導(dǎo)出表結(jié)構(gòu)的腳本或者是程序。這里選擇使用Golang
來(lái)從StarRocks
集群中導(dǎo)出所有數(shù)據(jù)庫(kù)的表結(jié)構(gòu)。
步驟
獲取所有的
Database
遍歷每一個(gè)
Database
獲取所有的表遍歷每一張表獲取每一張表的建表語(yǔ)句
因?yàn)?code>View的輸出結(jié)果和
Table
的數(shù)據(jù)結(jié)果不同,需要進(jìn)行分類判斷。將數(shù)據(jù)寫入文件,文件能直接使用
source
命令執(zhí)行。
可執(zhí)行的詳細(xì)代碼
如下:
package main import ( "database/sql" "errors" "fmt" "log" "os" _ "github.com/go-sql-driver/mysql" "github.com/spf13/pflag" "github.com/spf13/viper" ) type Databases struct { Database string } type Tables struct { Tables string } type CreateTable struct { Table string CreateString string Character_set_client string Collation_connection string } const ( defaultStarRocks = "127.0.0.1" defaultPort = 9030 defaultUserName = "root" defaultOutputFile = "ddl.sql" ) func main() { var starRocks, userName, password, outputFile string var port int var includedSys bool pflag.StringVarP(&starRocks, "starrocks", "s", defaultStarRocks, "The address of starrocks without a database name.") pflag.IntVarP(&port, "port", "p", defaultPort, "The port of starrocks") pflag.StringVarP(&userName, "userName", "u", defaultUserName, "The use name of starrocks") pflag.StringVarP(&password, "password", "w", "", "The password of starrocks") pflag.StringVarP(&outputFile, "outputFile", "o", defaultOutputFile, "The output file name.") pflag.BoolVarP(&includedSys, "includedSys", "i", false, "Included the system database or not, default is not.") pflag.Parse() viper.BindPFlag("starrocks", pflag.Lookup("starrocks")) viper.BindPFlag("port", pflag.Lookup("port")) viper.BindPFlag("userName", pflag.Lookup("userName")) viper.BindPFlag("password", pflag.Lookup("password")) viper.BindPFlag("outputFile", pflag.Lookup("outputFile")) viper.BindPFlag("includedSys", pflag.Lookup("includedSys")) jdbcURL := fmt.Sprintf("%s:%s@tcp(%s:%d)/", userName, password, starRocks, port) sysDB := []string{"_statistics_", "information_schema", "sys"} databases := getDatabases(jdbcURL) if _, err := os.Stat(outputFile); err == nil { // File exists, remove it errRemove := os.Remove(outputFile) if errRemove != nil { log.Println("Error removing file:", errRemove) return } log.Println("File removed successfully.") } for _, db := range databases { if !includedSys && containsElement(sysDB, db) { continue } writeToFile(outputFile, fmt.Sprintf("-- Database: %s\n", db)) writeToFile(outputFile, fmt.Sprintf("use %s;\n", db)) tables := getTables(jdbcURL, db) for _, table := range tables { if table != "" { createTable := getCreateTable(jdbcURL, db, table) for k, v := range createTable { var objectType string if v == "VIEW" { objectType = "View" } else if v == "TABLE" { objectType = "Table" } writeToFile(outputFile, fmt.Sprintf("-- %s: %s\n", objectType, table)) writeToFile(outputFile, fmt.Sprintf("-- Create %s: \n", objectType)) writeToFile(outputFile, fmt.Sprintf("%s\n", k)) } } } } } func writeToFile(outputFile string, s string) { file, err := os.OpenFile(outputFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { fmt.Println("Error opening file:", err) return } defer file.Close() _, err = file.Write([]byte(s)) if err != nil { fmt.Println("Error writing to file:", err) return } } func containsElement(list []string, target string) bool { for _, element := range list { if element == target { return true } } return false } func getDatabases(jdbcURL string) []string { db, err := sql.Open("mysql", jdbcURL) if err != nil { log.Fatal(err) } defer db.Close() res, err := db.Query("show databases") if err != nil { log.Fatal(err) } var databases []string for res.Next() { var ds Databases err := res.Scan(&ds.Database) if err != nil { log.Fatal(err) } databases = append(databases, ds.Database) } return databases } func getTables(jdbcURL, dbName string) []string { db, err := sql.Open("mysql", jdbcURL+dbName) if err != nil { log.Fatal(err) } defer db.Close() res, err := db.Query("show tables") if err != nil { log.Fatal(err) } var tables []string for res.Next() { var ts Tables err := res.Scan(&ts.Tables) if err != nil { log.Fatal(err) } tables = append(tables, ts.Tables) } return tables } func getCreateTable(jdbcURL, dbName, tableName string) map[string]string { db, err := sql.Open("mysql", jdbcURL+dbName) if err != nil { log.Fatal(err) } defer db.Close() res, err := db.Query("show create table " + tableName) if err != nil { log.Fatal(err) } createTables := make(map[string]string) for res.Next() { var ct CreateTable cs, _ := res.Columns() err := errors.New("") if len(cs) == 4 { // for view err = res.Scan(&ct.Table, &ct.CreateString, &ct.Character_set_client, &ct.Collation_connection) if err != nil { log.Fatal(err) } createTables[ct.CreateString] = "VIEW" } else if len(cs) == 2 { // for table err = res.Scan(&ct.Table, &ct.CreateString) if err != nil { log.Fatal(err) } createTables[ct.CreateString] = "TABLE" } } return createTables }
結(jié)果樣例
執(zhí)行完成后,結(jié)果樣例:
-- Database: test use test; -- Table: test_table -- Create Table: CREATE TABLE `test_table` ( `day` date NULL COMMENT "天", `id` varchar(65533) NULL COMMENT "id", `amount` double NULL COMMENT "值", `insert_time` varchar(65533) NULL COMMENT "數(shù)據(jù)插入時(shí)間" ) ENGINE=OLAP UNIQUE KEY(`day`, `id`) PARTITION BY RANGE(`day`) (PARTITION p20231020 VALUES [("2023-10-20"), ("2023-10-21")), PARTITION p20231021 VALUES [("2023-10-21"), ("2023-10-22")), PARTITION p20231022 VALUES [("2023-10-22"), ("2023-10-23")), PARTITION p20231023 VALUES [("2023-10-23"), ("2023-10-24")), PARTITION p20231024 VALUES [("2023-10-24"), ("2023-10-25")), PARTITION p20231025 VALUES [("2023-10-25"), ("2023-10-26")), PARTITION p20231027 VALUES [("2023-10-27"), ("2023-10-28")), PARTITION p20231028 VALUES [("2023-10-28"), ("2023-10-29")), PARTITION p20231029 VALUES [("2023-10-29"), ("2023-10-30")), PARTITION p20231030 VALUES [("2023-10-30"), ("2023-10-31"))) DISTRIBUTED BY HASH(`id`) PROPERTIES ( "replication_num" = "1", "dynamic_partition.enable" = "false", "dynamic_partition.time_unit" = "DAY", "dynamic_partition.time_zone" = "Asia/Shanghai", "dynamic_partition.start" = "-7", "dynamic_partition.end" = "3", "dynamic_partition.prefix" = "p", "dynamic_partition.buckets" = "1", "dynamic_partition.history_partition_num" = "0", "in_memory" = "false", "enable_persistent_index" = "false", "replicated_storage" = "true", "compression" = "LZ4" ); -- View: test_view -- Create View: CREATE VIEW `test_view` (`day`, `id`, `amount`, `insert_time`) AS SELECT `test`.`test_table`.`day`, `test`.`test_table`.`id`,`test`.`test_table`.`amount`, `test`.`test_table`.`insert_time` FROM `test`.`test_table` WHERE `test`.`test_table`.`day` >= '2023-11-07' ;
未實(shí)現(xiàn):
備份MV
備份UDF
備份用戶角色和grant信息。
備份Catalog的創(chuàng)建語(yǔ)句。
以上代碼使用go
版本為1.21.4
,其他版本可能會(huì)有些不同,更多關(guān)于go備份StarRocks建表語(yǔ)句的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
go語(yǔ)言yaml轉(zhuǎn)map、map遍歷的實(shí)現(xiàn)
本文主要介紹了go語(yǔ)言yaml轉(zhuǎn)map、map遍歷的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Go語(yǔ)言實(shí)現(xiàn)類似c++中的多態(tài)功能實(shí)例
Go本身不具有多態(tài)的特性,不能夠像Java、C++那樣編寫多態(tài)類、多態(tài)方法。但是,使用Go可以編寫具有多態(tài)功能的類綁定的方法。下面來(lái)一起看看吧2016-09-09GoZero中make后返回?cái)?shù)據(jù)與原數(shù)據(jù)不對(duì)齊的幾種解決方案
在Go語(yǔ)言中,make是用來(lái)創(chuàng)建切片、映射(map)和通道(channel)的內(nèi)建函數(shù),但是,在使用 make 創(chuàng)建切片時(shí),若不理解如何正確使用其返回值,可能會(huì)遇到數(shù)據(jù)對(duì)不上或結(jié)果不符合預(yù)期的情況,本文將分析在GoZero或其他基于Go的應(yīng)用中,使用make時(shí)可能導(dǎo)致的問(wèn)題及解決方案2025-01-01Golang實(shí)現(xiàn)組合模式和裝飾模式實(shí)例詳解
這篇文章主要介紹了Golang實(shí)現(xiàn)組合模式和裝飾模式,本文介紹組合模式和裝飾模式,golang實(shí)現(xiàn)兩種模式有共同之處,但在具體應(yīng)用場(chǎng)景有差異。通過(guò)對(duì)比兩個(gè)模式,可以加深理解,需要的朋友可以參考下2022-11-11一文帶你掌握Go語(yǔ)言I/O操作中的io.Reader和io.Writer
在?Go?語(yǔ)言中,io.Reader?和?io.Writer?是兩個(gè)非常重要的接口,它們?cè)谠S多標(biāo)準(zhǔn)庫(kù)中都扮演著關(guān)鍵角色,下面就跟隨小編一起學(xué)習(xí)一下它們的使用吧2025-01-01golang如何用type-switch判斷interface變量的實(shí)際存儲(chǔ)類型
這篇文章主要介紹了golang如何用type-switch判斷interface變量的實(shí)際存儲(chǔ)類型,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04go微服務(wù)PolarisMesh源碼解析服務(wù)端啟動(dòng)流程
這篇文章主要為大家介紹了go微服務(wù)PolarisMesh源碼解析服務(wù)端啟動(dòng)流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01golang在GRPC中設(shè)置client的超時(shí)時(shí)間
這篇文章主要介紹了golang在GRPC中設(shè)置client的超時(shí)時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04Golang實(shí)現(xiàn)for循環(huán)運(yùn)行超時(shí)后自動(dòng)退出的方法
for循環(huán)對(duì)大家來(lái)說(shuō)應(yīng)該都不陌生,對(duì)于golang來(lái)說(shuō)更是必不可少,所以下面這篇文章就來(lái)給大家介紹了關(guān)于Golang如何實(shí)現(xiàn)for循環(huán)運(yùn)行一段時(shí)間超時(shí)后自動(dòng)退出的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11