Golang对接Hive工具——gohive

背景

在对接Hive组件的开发实践,探索学习了golang操作hive的工具库,并实现了数据中心对接hive、操作hive数据集、导出下载hive表等功能。

但golang自带的sql库无法直接对hive进行操作,也没有官方库提供,网上又少有此类工具库的实现,经过调研,决定使用gohive。

golang操作Hive库

使用了gohive库操作tbds的Hive库表,gohive是golang操作Hive和Spark分布式SQL引擎的driver,提供两种验证机制:

  • 支持不带鉴权连接Hive;
  • 支持带有SSL的连接机制,包括kerberos、LDAP、CUSTOM和NOSASL

以上两种验证机制都支持http或者二进制的传输。

连接

创建连接(省略了错误处理):

1
2
3
4
5
6
configuration := gohive.NewConnectConfiguration()
configuration.Username = "username"
configuration.Password = "password"
configuration.Database = "dbName"

conn, _ := gohive.Connect("hostname", 10000, "LDAP", configuration)

Connect函数内部实现了类似ping的验证,所以Connect返回实例不为空,则连接成功。

使用query语句

在使用gohive拿到连接实例conn后,可通过conn的cursor来执行查询语句,并在查询结束后手动关闭cursor:

1
2
3
4
5
6
ctx := context.Background()
cursor := conn.Cursor()
defer cursor.Close()

query := fmt.Sprintf("select * from %s", tableName)
cursor.Exec(ctx, query)

结果集包含在cursor实例中。

结果集解析

gohive通过cursor,即类似光标指针的方式,来操作查询和获取结果集。对于结果集的遍历赋值,gohive提供了2种方式:

使用RowMap

1
2
3
4
5
6
for cursor.HasMore(ctx) {
row := cursor.RowMap(ctx)
for k, v := range row {
key := fmt.Sprintf("col: %s, value: %s", k, v)
}
}

RowMap返回key为列名,value为表数据值的map[string]interface{},例如:

user_id user_name
1 zhangsan
2 lisi

则随着光标后移(HasMore),RowMap:

  • 第一次返回{“user_id”: 1, “user_name”:”zhangsan”}
  • 第二次返回{“user_id”: 2, “user_name”: “lisi”}

指定参数赋值

上面的表结构,若已知结果集有参数数量和参数类型,可以直接使用FetchOne指定参数赋值:

1
2
3
4
5
6
7
var userId int
var userName string

for cursor.HasMore(ctx) {
cursor.FetchOne(ctx, &userId, &userName)
fmt.printf("id: %d, name: %s\n", userId, userName)
}

输出:

1
2
id: 1, name: zhangsan
id: 2, name: lisi

这种方式若FetchOne传参个数不对,会报参数个数不匹配的错误。

遇到的问题

虽然gohive提供了方便连接和操作Hive库的方式,但是在使用gohive工具实践的过程中,同样也遇到了一些问题。

连接时间过长

在测试中发现,查询一张表的总行数时,请求响应时间竟然长达10秒:

gohive-long-conn

经过排查,发现有2处操作比较耗时:

session-retablished

  1. 一是Hive引擎自身处理行数查询时(图中下面的红框框3.38秒),会起spark执行map-reduce任务,由于引擎本身与mysql存在差异,这部分时间无法优化(但是可以采用异步的方式,后面会介绍);
  2. 二是与Hive引擎重新建立连接的时间每次高达6秒,优化点可以将conn实例缓存起来,虽然长时间不使用会断开,但在timeout时间内做频繁查询则不需重新建立连接,一定程度上会起到查询优化的作用。

同步查询时间过长

cursor.Exec(ctx)使用的是同步查询,对于处理时间过长的查询,gohive提供了异步的查询接口,在查询过程中做其他的事,然后等待查询完成并解析结果集:

1
2
3
4
5
6
7
8
9
cursor.Execute(ctx, query, true /*async*/)
// do other things

cursor.WaitForCompletion(ctx)

// get cursor result
for cursor.HasMore(ctx) {
....
}

总结

本文介绍了使用golang操作Hive库表的工具gohive,以及分析了实践过程中遇到的问题和解决方案。