使用go連接clickhouse的實戰(zhàn)操作
前言
近段時間業(yè)務在一個局點測試clickhouse,用java寫的代碼在環(huán)境上一直連接不上clickhouse服務,報錯信息也比較奇怪,No client available,研發(fā)查了一段時間沒查出來,讓運維這邊繼續(xù)查:

運維同學查了各種監(jiān)聽配置,防火墻這些,都沒什么問題,但是沒有明確證據能夠提供證明通過http方式能訪問到數據庫,時間拖得比較久,項目上就急了,讓盡快找到問題,所以就用go寫了個小工具拉到集群上試試看8123這個端口到底能不能正常提供服務。
正文
先安裝必要的庫,clickhouse官方提供了2個版本的庫,v1和v2,v1版本已經明確不會繼續(xù)更新了,所以用新不用舊哈,可以用官方庫的方式或者用dsn的方式,這個我下面一起說,安裝庫的命令:

go get github.com/ClickHouse/clickhouse-go/v2
構造結構體
編寫結構體,存放基本信息:
type Clickhouse struct {
Host string // 服務端主機
Port int // 端口
DB string // 數據庫
User string // 用戶名
Password string // 密碼
Connection *sql.DB // 建立連接后存放連接
Rows *sql.Rows // 運行sql后的結果存放
}
Connection主要是用來建立連接后把相關信息存放,這樣方便繼續(xù)調用其他的方法,因為我的主要目的是測試數據庫能否連通和運行Sql,所以這里Rows用來存放測試的select語句的結果。
參數讀取
這塊沒什么好說的,連接的參數直接從命令行讀取,用flag包就好:
var (
host = flag.String("host", "localhost", "clickhouse host")
port = flag.Int("port", 8123, "clickhouse port")
user = flag.String("user", "default", "clichouse user")
pass = flag.String("password", "", "clickhouse password")
db = flag.String("db", "default", "clickhouse database")
query = flag.String("query", "show tables", "query you will run")
mode = flag.String("mode", "driver", "driver or dsn")
)
前面幾個參數不用解釋,主要是query和mode,query是要運行的sql語句,我們默認就認為跑的是select語句,然后是mode,允許選擇模式,用戶可以使用driver或者dsn兩種模式進行連接,我寫了兩個不同的方法,其實也可以在一個Connect方法里做判斷,看個人習慣;
建立連接
接下來我們建立數據庫連接:
//
func (c *Clickhouse) Conn() {
c.Connection = clickhouse.OpenDB(&clickhouse.Options{
Addr: []string{fmt.Sprintf("%s:%d", c.Host, c.Port)},
Auth: clickhouse.Auth{
Database: c.DB,
Username: c.User,
Password: c.Password,
},
Settings: clickhouse.Settings{
"max_execution_time": 60,
},
DialTimeout: 5 * time.Second,
Compression: &clickhouse.Compression{
Method: clickhouse.CompressionBrotli,
Level: 5,
},
// 必須添加協(xié)議方式
Protocol: clickhouse.HTTP,
})
}
func (c *Clickhouse) ConnDsn() {
conn, err := sql.Open("clickhouse", fmt.Sprintf("http://%s:%d/%s?username=%s&password=%s", c.Host, c.Port, c.DB, c.User, c.Password))
if err != nil {
log.Printf("Connect to the server failed, %s.\n", err.Error())
return
}
c.Connection = conn
}參考官網的實例,實現兩種連接方式,關閉方法就直接把sql.DB和sql.Rows都關閉就可以了:
func (c *Clickhouse) Close() {
c.Connection.Close()
c.Rows.Close()
}
發(fā)起查詢
查詢使用Query方法進行:
func (c *Clickhouse) Select(query string) {
rows, err := c.Connection.Query(query)
if err != nil {
log.Printf("Query select failed, %s.\n", err.Error())
return
}
c.Rows = rows
}
查詢的結果我保存到Rows里,方便后面的解析
結果解析
比較麻煩的就是結果的解析了,用過database/sql庫的哥們都知道,這個庫只提供了基礎的一些接口,查詢出來一般用Scan去獲取數據,用法類似這樣:

問題就在于,Scan要指定和sql查詢出來一樣多的變量,對于我們這個小工具來說,sql是不一定的,所以查詢出來的字段數量肯定yes不定的,如何動態(tài)處理這個問題,肯定是不能直接寫一個結構體解決的,先看我的代碼:
func (c *Clickhouse) Show() {
cols, err := c.Rows.Columns()
if err != nil {
log.Printf("Failed to get table columns, %s.\n", err.Error())
return
}
// 一行數據,使用any是為了避開數據類型的問題
var rows = make([]any, len(cols))
// 存實際的值,是byte數組,長度以列的數量為準
var values = make([][]byte, len(cols))
for i := 0; i < len(cols); i++ {
rows[i] = &values[i]
}
// 打印表頭
fmt.Println(strings.Join(cols, ","))
for c.Rows.Next() {
if err = c.Rows.Scan(rows...); err != nil {
fmt.Println(err)
return
}
var vString []string
for _, v := range values {
vString = append(vString, string(v))
}
// 逐行打印出來
fmt.Println(strings.Join(vString, ","))
}
}
大概思路是這樣:
- Scan需要傳入每個用來綁定單行數據值的變量,所以values是實際存儲數據的byte數組,然后把數組的每個元素的地址再存入到rows數組中;
- 現在可以用rows[index]這樣的方式來訪問values中的值了,把rows直接作為入參傳入到Scan,在每次循環(huán)中,把values的值轉成逗號分割的字符串,直接打印
結果驗證
OK,現在邏輯完成了,我們運行測試一下,
go run main.go -host hostname -password paswword -query "select * from clusters" -db system -mode dsn

只查詢2個字段,2行數據:

結語
完成,然后把工具放到生產環(huán)境一測試,查詢都正常,這下開發(fā)哥們要繼續(xù)查他的程序問題了,??,運維甩鍋成功??????
到此這篇關于使用go連接clickhouse的實戰(zhàn)操作的文章就介紹到這了,更多相關go連接clickhouse內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go 循環(huán)結構for循環(huán)使用教程全面講解
這篇文章主要為大家介紹了Go 循環(huán)結構for循環(huán)使用全面講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10

