ClickHouse连接失败主因是协议与端口不匹配:默认clickhouse-go走HTTP(8123),但生产环境常仅开放TCP(9000);应改用tcp://DSN、确认服务端tcp_port启用,或显式设secure=false;空结果多因未检查rows.Err()及字段顺序/类型不匹配;批量插入须用PrepareBatch而非单条执行;GROUP BY需显式AS别名并按序Scan;务必核对驱动与服务端版本兼容性。

用 github.com/ClickHouse/clickhouse-go 连 ClickHouse 时连不上
默认配置下 clickhouse-go 会尝试走 HTTP 协议,但很多生产 ClickHouse 实例只开放 TCP(9000 端口)且禁用 HTTP(8123),连不上八成是协议和端口对不上。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 确认服务端监听协议:查
/etc/clickhouse-server/config.xml里<http_port>和<tcp_port>是否都启用,多数线上环境只开tcp_port - 客户端改用 TCP 驱动:初始化 DSN 时用
tcp://前缀,例如tcp://127.0.0.1:9000?database=default,别用http:// - 如果必须走 HTTP,确保服务端开了
http_port,且 DSN 是http://127.0.0.1:8123,同时加&compress=true减少传输量 - 注意新版驱动(v2+)默认要求 TLS,若服务端没配证书,得显式关掉:
&secure=false&insecure=true
Query 返回空结果但没报错,数据明明存在
ClickHouse 的 SELECT 在 Go 中用 rows.Scan() 逐行读取时,最容易漏掉两件事:没调用 rows.Err() 检查扫描异常,以及字段顺序和类型跟表结构不一致。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 永远在
for rows.Next()循环结束后加if err := rows.Err(); err != nil { ... },很多“空结果”其实是扫描中途 panic 或静默失败 - 别依赖 SELECT *,明确写出字段名,并按顺序传入
Scan()的变量地址,例如表是(id UInt64, name String, created DateTime),就要rows.Scan(&id, &name, &created) - 时间类型优先用
time.Time接收,但注意 ClickHouse 的DateTime默认时区是 UTC,而 Gotime.Now()是本地时区,比较前先统一时区 - 字符串字段用
*string而非string,避免 NULL 值触发 panic
批量插入百万级数据太慢,CPU 和网络打满
用 stmt.Exec() 单条插、或拼大 SQL 字符串,都会让 ClickHouse 写入变成随机小写,无法发挥列式存储的批处理优势。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 必须用
conn.PrepareBatch()构建批处理对象,再循环调用batch.Append(),最后batch.Send()一次性提交 - 每批控制在 1w–10w 行之间,太小吞吐上不去,太大内存压力高;可通过
clickhouse.BatchSettings{MaxWait: 5 * time.Second}控制超时 - 避免在循环里反复调用
conn.PrepareBatch(),它本身有连接开销,应复用 batch 实例 - 如果数据来自 CSV 或 JSON,直接用
clickhouse-go的conn.SendStream()配合gzip.Reader更快,绕过 Go 层解析
GROUP BY 查询结果字段顺序错乱或类型不匹配
ClickHouse 的 GROUP BY 结果集 schema 不完全由 SELECT 列决定,尤其用了聚合函数(count()、sum())后,返回字段名可能带别名或隐式重命名,Go 驱动不会自动映射到 struct tag。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 所有聚合查询显式加
AS别名,例如count(*) AS total,然后 Scan 时严格按 SELECT 后的顺序绑定变量 - 别用
sqlx.StructScan,它依赖列名反射匹配,而 ClickHouse 返回的列名可能是count()这种非法标识符;老实用rows.Scan()+ 位置绑定 - 执行前先用
rows.Columns()打印实际列名和类型,确认是否符合预期,比如sum(value)可能返回sum(value)而不是value_sum - 聚合结果中的
Nullable(UInt64)类型,在 Go 里得用*uint64接收,否则 Scan 会失败
ClickHouse 对 Go 来说不是“换个驱动就能跑”的数据库,它的类型系统、协议细节、批处理模型都和传统关系型库差异很大。最常被忽略的是:驱动版本和服务端版本的兼容性——v2 驱动不支持低于 21.8 的 ClickHouse,而 v1 驱动又不支持原生 LZ4 压缩。上线前务必核对 SELECT version() 和 go.mod 里的驱动版本号。










