ClickHouse连接失败主因是协议不匹配:v2驱动默认启用TLS和LZ4压缩,而本地单机版仅支持HTTP(8123)或裸TCP(9000),需显式设secure=false&compress=false;TCP连接必须用9000端口,HTTP则用8123且不支持上述参数。

连不上 ClickHouse 几乎从来不是网络或密码错了,而是 clickhouse-go/v2 默认走 TLS + LZ4 压缩,而本地单机版默认只开 HTTP(8123)或裸 TCP(9000 无加密),协议直接不匹配。
连接失败:secure=false 和 compress=false 必须显式加
官方驱动 v2 版本默认启用 secure=true(即强制 TLS)和 compress=true(LZ4),但你本地用 docker run yandex/clickhouse-server 起的服务压根没配证书,也不开 LZ4 解压支持——结果就是握手卡死,日志里只报 context deadline exceeded,根本看不出是 TLS 搞的鬼。
- TCP 连接必须用端口
9000,连接串写成tcp://127.0.0.1:9000?database=default&secure=false&compress=false - 如果硬要用 HTTP 接口(
8123),得换协议:http://127.0.0.1:8123?database=default,此时secure和compress参数无效,别加 - 查连接是否生效,别只看
sql.Open返回 nil;一定要db.Ping(),否则连接池懒加载会把问题拖到第一次Exec才暴露
批量写入慢如爬:别用 db.Exec 拼单条 INSERT
用 db.Exec("INSERT ... VALUES (?, ?)", a, b) 插 10 万行,本质是发 10 万次 TCP 包,每条都走 SQL 解析、类型推导、小块 merge——ClickHouse 的设计哲学是“大块写入”,这么干等于反着来。
- 必须用
conn.PrepareBatch()获取Batch对象,再调batch.Append()累数据,最后batch.Send()一次性发出去 - 每批控制在
1000–10000行之间:太小吞吐上不去,太大可能触发服务端max_insert_block_size限制或客户端 OOM - 如果数据来自 CSV/JSON 流,优先用
conn.Writer().Write(),它底层复用连接+缓冲,比Batch还快 3–5 倍
读 Nullable(String) 字段 panic:“cannot scan into *string”
ClickHouse 的 Nullable(String) 不等于 Go 的 *string。驱动不会自动帮你判空转义,直接扫进 *string 就 panic,错误信息就一句 cannot scan into *string,非常误导。
- 字段声明为
Nullable(String)时,Go 端必须用sql.NullString或 v2 驱动提供的ch.String(需import "github.com/ClickHouse/clickhouse-go/v2/lib/columns") - 如果业务上该字段确实不允许为空,建表时就该定义为
String,而不是靠应用层补if v.Valid -
rows.Scan()不校验列数,也不自动分配切片容量;务必先调rows.Columns()看字段名和类型,尤其 SQL 里用了AS别名或聚合函数时
查询结果不准:GROUP BY / ORDER BY 失效的隐藏开关
在分布式表或带 JOIN 的查询中,Go 客户端拿到的结果可能只是某一分片的局部结果,COUNT() 偏低、ORDER BY 乱序——这不是驱动 bug,而是 ClickHouse 服务端默认开启的优化开关在作祟。
- SQL 开头加
SETTINGS distributed_group_by_no_merge = 0,强制服务端合并所有分片结果再返回 - 类似地,
optimize_sorting_by_input = 0可禁用输入排序优化,避免 ORDER BY 被跳过 - 这些 setting 是 session 级的,只对当前 query 生效,不影响其他请求
最常被忽略的其实是连接串里的协议和端口对应关系:TCP 是 9000,HTTP 是 8123,驱动默认走 TCP,但很多人复制 HTTP 的 curl 示例去配 TCP 连接,一配一个错。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











