Go 语言操作金仓数据库(上篇):环境搭建与连接管理
从一个实际问题说起
去年写一个数据采集服务,用 Go 语言开发,需要连金仓数据库。项目工期紧,心想 Go 的标准库 database/sql 应该能直接支持吧?结果一查,发现金仓的 Go 驱动需要单独配置,而且网上资料不多。
折腾了一天多才跑通。今天把过程整理出来,分成上下两篇。上篇讲环境搭建和连接管理,下篇讲 SQL 执行和高级特性。
一、Gokb 驱动是什么
Gokb 是金仓官方提供的 Go 语言驱动,完全用 Go 编写,实现了 database/sql 接口。这意味着你只需要导入这个驱动,剩下的操作都用 Go 标准库的方式写就行。
驱动的特点:
- 纯 Go 实现,没有 CGO 依赖
- 支持
database/sql标准接口 - 支持连接池、预备语句、事务等常用功能
- 支持多主机高可用配置
官方推荐直接使用 database/sql 包,不直接调用驱动内部接口。
二、环境搭建
2.1 安装 Go
首先得把 Go 环境装好。去 Go 官网下载对应操作系统的安装包。
Linux 下安装:
bash
# 解压
tar -zxvf go1.20.linux-amd64.tar.gz
# 配置环境变量
export PATH=/path/to/go/bin:$PATH
# 验证
go version
2.2 两种包管理方式
Gokb 支持 GOPATH 和 Go Module 两种方式。推荐用 Go Module,更现代。
方式一:Go Module(推荐)
先初始化项目:
bash
go mod init myproject
项目根目录会生成 go.mod 文件,内容大致如下:
arduino
module myproject
go 1.18
把解压后的 Gokb 驱动源码放到任意目录,比如 ./gokb/。然后在 go.mod 中添加 replace 指令:
bash
module myproject
go 1.18
require kingbase.com/gokb v1.0.0
replace kingbase.com/gokb => ./gokb
最后拉取依赖:
bash
go mod tidy
这个命令会自动下载 Gokb 依赖的其他包(比如 decimal、civil 等)。
方式二:GOPATH
需要先把 GO111MODULE 关了:
bash
export GO111MODULE=off
然后把 Gokb 源码放到 $GOPATH/src/kingbase.com/gokb 目录下。手动下载依赖包并拷贝到 $GOPATH/src。
我建议用第一种,省事。
2.3 导入驱动
在代码中通过 _ 方式导入驱动,这样驱动会自动注册到 database/sql:
go
import (
"database/sql"
_ "kingbase.com/gokb" // 匿名导入,只执行 init 函数
)
三、连接数据库
3.1 基本连接
和其他语言不同,sql.Open 不会立即建立连接,它只是解析连接串、创建 db 对象。真正连接是在第一次使用时建立的。
所以 Open 之后要调用 Ping 验证连接是否成功。
go
package main
import (
"database/sql"
"fmt"
_ "kingbase.com/gokb"
)
const (
host = "127.0.0.1"
port = 54321
user = "system"
password = "123456"
dbname = "TEST"
)
func main() {
connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
host, port, user, password, dbname)
db, err := sql.Open("kingbase", connStr)
if err != nil {
panic(err)
}
defer db.Close()
// 重要:验证连接是否成功
err = db.Ping()
if err != nil {
panic(err)
}
fmt.Println("连接成功!")
}
3.2 连接参数详解
连接字符串是键值对格式,空格分隔。常用参数:
| 参数 | 说明 | 默认值 |
|---|---|---|
| host | 服务器地址 | localhost |
| port | 端口 | 54321 |
| user | 用户名 | 无 |
| password | 密码 | 无 |
| dbname | 数据库名 | 同用户名 |
| sslmode | SSL模式 | require |
| connect_timeout | 连接超时(秒) | 0(无限) |
| keepalive_interval | 保活探测间隔(秒) | 15 |
如果参数值包含空格,用单引号括起来:
go
// 用户名是 "space man"
connStr := `user='space man' password='it''s valid' dbname=TEST`
3.3 多主机配置
生产环境通常是集群部署,可以配置多个主机实现故障转移:
go
// 两个主机不同端口
connStr := "host=192.168.1.100,192.168.1.101 port=54321,54322 user=system password=123456 dbname=TEST"
// 两个主机同端口
connStr := "host=192.168.1.100,192.168.1.101 port=54321 user=system password=123456 dbname=TEST"
也可以配合重试参数:
go
connStr := "host=192.168.1.100,192.168.1.101 port=54321 user=system password=123456 dbname=TEST retry=3 delay=2"
retry=3 表示一轮所有主机都失败后,再重新尝试 3 次。delay=2 表示每次重试前等待 2 秒。
3.4 只连主节点
如果只想连接主节点(读写节点),配置 target_session_attrs=read-write:
go
connStr := "host=192.168.1.100,192.168.1.101 port=54321 user=system password=123456 dbname=TEST target_session_attrs=read-write"
驱动会依次尝试每个主机,直到连到一个支持读写的主节点。
3.5 SSL 配置
go
// 禁用 SSL
connStr := "host=127.0.0.1 user=system password=123456 dbname=TEST sslmode=disable"
// 开启 SSL
connStr := "host=127.0.0.1 user=system password=123456 dbname=TEST sslmode=require"
// 使用证书
connStr := "host=127.0.0.1 user=system password=123456 dbname=TEST sslmode=verify-full sslcert=client.crt sslkey=client.key sslrootcert=ca.crt"
四、连接池管理
Go 的 database/sql 自带连接池,不用第三方库。但默认参数需要根据业务调整。
4.1 连接池参数
go
// 设置最大打开连接数(默认无限)
db.SetMaxOpenConns(20)
// 设置最大空闲连接数(默认2)
db.SetMaxIdleConns(10)
// 设置连接最大存活时间
db.SetConnMaxLifetime(time.Hour)
// 设置空闲连接最大存活时间
db.SetConnMaxIdleTime(10 * time.Minute)
生产环境建议配置这些参数,避免连接数过高导致数据库压力大。
4.2 连接池行为说明
db 对象代表一个连接池,可以被多个 goroutine 安全地并发使用。Go 会自动管理连接的创建和回收。
有两个特殊情况需要注意:
- 调用
Begin()开启事务后,返回的Tx对象会绑定到单个连接,直到Commit()或Rollback()后才释放。 - 调用
Query()返回的Rows对象也会占用连接,需要用defer rows.Close()释放。
go
// 正确写法:用 defer 确保释放
rows, err := db.Query("SELECT * FROM users")
if err != nil {
return err
}
defer rows.Close() // 重要!
for rows.Next() {
// 处理数据
}
4.3 关闭连接
go
db.Close()
但 db 对象是为长连接设计的,不要频繁 Open 和 Close。通常程序启动时 Open 一次,程序退出时 Close。
五、完整示例
下面是一个完整的连接示例,包含了连接池配置:
go
package main
import (
"database/sql"
"fmt"
"time"
_ "kingbase.com/gokb"
)
const (
host = "127.0.0.1"
port = 54321
user = "system"
password = "123456"
dbname = "TEST"
)
func main() {
connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable connect_timeout=10",
host, port, user, password, dbname)
db, err := sql.Open("kingbase", connStr)
if err != nil {
panic(fmt.Sprintf("打开数据库失败: %v", err))
}
defer db.Close()
// 配置连接池
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(time.Hour)
db.SetConnMaxIdleTime(10 * time.Minute)
// 验证连接
err = db.Ping()
if err != nil {
panic(fmt.Sprintf("连接数据库失败: %v", err))
}
fmt.Println("连接成功!")
fmt.Printf("连接池状态: MaxOpen=%d, MaxIdle=%d\n", db.Stats().MaxOpenConnections, db.Stats().Idle)
}
六、常见问题
6.1 驱动注册失败
报错 driver: unknown driver "kingbase",说明驱动没注册成功。检查是否正确导入了 _ "kingbase.com/gokb"。
6.2 连接超时
可以设置 connect_timeout 参数:
go
connStr := "host=127.0.0.1 user=system password=123456 dbname=TEST connect_timeout=10"
6.3 连接断开后无法自动重连
Go 的连接池默认不会自动重连。可以用 SetConnMaxLifetime 定期回收连接,或者用 ping 检测:
go
// 定期 ping 检测连接是否正常
go func() {
ticker := time.NewTicker(30 * time.Second)
for range ticker.C {
if err := db.Ping(); err != nil {
log.Printf("ping 失败: %v", err)
}
}
}()
七、小结
上篇主要讲了:
- 环境搭建:Gokb 驱动安装、Go Module 配置、驱动导入
- 连接数据库:连接字符串格式、参数说明、多主机配置
- 连接池管理:参数设置、行为说明、事务和 Rows 的特殊处理
下篇会讲 SQL 执行、预备语句、类型映射、超时控制等实战内容。