可行但需注意协议与版本差异:clickhouse-go v2 默认走TCP(9000端口),不注册database/sql,须显式启用兼容层;v1无此支持,HTTP需反代8123端口;批量插入用BatchInsert,字段类型严格匹配,ScanStruct仅识别ch:标签且Nullable需用指针接收。

Go 用 clickhouse-go 连 ClickHouse 是可行的,但默认驱动不支持 HTTP 协议,且 v2 版本 API 和 v1 完全不兼容——多数人卡在这儿。
为什么 database/sql 打不开 ClickHouse?
ClickHouse 官方不提供标准 SQL 驱动,clickhouse-go 是纯 Go 实现的原生 TCP 驱动,它**不注册到 database/sql 接口**。你写 sql.Open("clickhouse", "...") 会 panic:unknown driver "clickhouse"。
- 想用
database/sql必须上clickhouse-go/v2的sql.Open兼容层(需显式启用) - v1(
github.com/kshvakov/clickhouse)压根没这层,直接用clickhouse.Open - HTTP 模式(比如用 Nginx 反代 8123 端口)v2 默认不支持,得手动配
clickhouse.WithHTTP
clickhouse-go/v2 连接失败的三个高频原因
连不上不是网络问题,大概率是协议或参数错位:
-
Addr写成"localhost:8123"—— 这是 HTTP 端口,v2 默认走 TCP(9000),得改成"localhost:9000" - 用了
clickhouse.Compression(clickhouse.CompressionLZ4)但服务端没开 LZ4 支持(查SELECT * FROM system.settings WHERE name LIKE '%compression%') - 用户名密码正确,但没指定
Database参数,而 ClickHouse 默认库是default,若连的是其他库却漏写,会报Code: 86. DB::Exception: Database ... doesn't exist
INSERT 性能差?别用单条 stmt.Exec
ClickHouse 不是 MySQL,单行 INSERT 基本等于自废武功。v2 的批量写法和 v1 差异极大:
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- v2 推荐用
conn.BatchInsert()+batch.Append(...),每 batch 控制在 1k–10k 行较稳 - 别用
tx.Stmt().Exec循环插入,哪怕加了Prepared: true,性能仍差一个数量级 - 字段类型要严格匹配:Go 的
int64对应 ClickHouse 的Int64,但uint32写进UInt32字段会静默转成负数(因底层按有符号处理)
查询返回 nil 或 panic?检查 ScanStruct 的字段标签
v2 的 rows.ScanStruct 不认 json: 标签,只认 ch::
type Event struct {
ID uint64 `ch:"id"`
Name string `ch:"name"`
Ts time.Time `ch:"event_time"`
}如果漏写 ch: 或拼错字段名(比如 ClickHouse 列是 event_time,结构体里写成 EventTime 却没标 ch:"event_time"),ScanStruct 会跳过该字段,不报错也不赋值,结果就是零值。
另外,Nullable(UInt64) 类型必须用 *uint64 接收,用 uint64 会 panic:cannot scan into dest 0: cannot assign <nil> to uint64</nil>。
ClickHouse 的类型系统和 Go 的空值语义对不上,字段定义、扫描方式、批量边界——这三个地方一松动,就容易出不可见的脏数据或静默失败。

















