用golang开发服务端程序,开发效率和程序运行效率都还蛮不错。但是有些功能实现起来,也还是挺蛋疼的。
最近,由于数据库中间层项目需要监测从库的同步状态,屏蔽同步故障和同步延迟较大的从库,因此需要监测从库的slave status。这个功能用其它语言实现起来非常简单,但是golang却把笔者卡住了半天时间。试来试去,最终总算解决(汗)。
大体过程如下:
查看从库状态,大家都知道用
1 |
SHOW SLAVE STATUS |
,golang代码写起来也不难:
1 |
rows, err := db.Query("SHOW SLAVE STATUS") |
获取结果的时候问题来了,不同版本的MySQL返回的结果列数不一样,笔者用的MySQL是官方5.5版本,会返回40列数据,肯定不能用枚举的方式。OK,先试试字符串Slice。
1 2 3 4 5 |
cols, _ := rows.Columns() data := make([]string, len(cols)) for rows.Next() { rows.Scan(data...) } |
程序运行报错:
1 |
cannot use data (type []string) as type []interface {} in function argument |
貌似只能接受interface{}类型的参数,那咱换下变量类型:
1 2 3 4 5 |
cols, _ := rows.Columns() data := make([]interface{}, len(cols)) for rows.Next() { rows.Scan(data...) } |
运行还是报错(晕…):
1 |
sql: Scan error on column index 0: destination not a pointer |
貌似无解了~~(抓狂中,此处省略N字)
一顿google之后,找到一种解法:
1 2 3 4 5 6 7 8 9 |
buff := make([]interface{}, len(cols)) // 临时slice,用来通过类型检查 data := make([]string, len(cols)) // 真正存放数据的slice for i, _ := range buff { buff[i] = &data[i] // 把两个slice关联起来 } for rows.Next() { rows.Scan(buff...) } |
运行一下试试,泪流满面啊……
期待中的结果终于出来了。把列名加上一起输出:
1 2 3 |
for k, col := range data { fmt.Printf("%10s:\t%10s\n", cols[k], col) } |
这种方式也能用于获取返回列数不定的场景。
最后: 或许是笔者的功力太浅,目前只能找到这种解决办法,如果那位大侠有更优雅的解决方案,希望告知我一下。
下面是完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
func slaveInfo() { db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8&timeout=%s", USER, PASS, HOST, PORT, TOUT)) if err != nil { log.Fatalf("Mysql DSN Error: %v\n", err) } if err = db.Ping(); err != nil { log.Fatalf("Fail to connect to mysql: %v\n", err) } defer db.Close() rows, err := db.Query("SHOW SLAVE STATUS") if err != nil { log.Fatalf("Query fail: %v\n", err) } cols, _ := rows.Columns() buff := make([]interface{}, len(cols)) // 临时slice data := make([]string, len(cols)) // 存数据slice for i, _ := range buff { buff[i] = &data[i] } for rows.Next() { rows.Scan(buff...) // ...是必须的 } for k, col := range data { fmt.Printf("%30s:\t%s\n", cols[k], col) } } |
4 Comments
sql: Scan error on column index 0: destination not a pointer
这个应该是你db库打印的,他里面怎么判断处理的?go不是很懂,传入data(interface{})和&data(interface{}类型)是一样的吗?
sql这个错误是标准库输出的。Scan的时候返回err信息。
汗,太低调了,你都用go写大型高并发程序了,还不很懂……
data是slice类型,也可以理解为指向一个array的指针。
&data就可以理解为指针的指针了,和slice类型还不一样。如果上面程序中传入&data,结果会报错:
研究了一下Scan的源码,主要涉及到的代码如下:
convertAssign函数主要过程如下:
Scan函数中如果只是传入[]interface{}类型的变量,在convertAssign函数里,dest的类型就是interface{}了,直接返回错误。
Thank you for the code. Saved my butt.