连不上clickhouse主因是协议端口不匹配:tcp默认9000,http需8123且须显式设protocol;insert应优先用preparebatch分批1万–10万行;nullable字段必用sql.nullstring,时区需服务端客户端对齐;查询必须双超时防护——context.withtimeout与sql中settings max_execution_time。

连不上 ClickHouse?先看协议和端口对不对
绝大多数连接失败不是密码错,而是驱动默认协议和服务端暴露的端口不匹配。clickhouse-go v2 默认走 TCP 协议(9000 端口),但本地开发常只开 HTTP(8123),云服务又可能只开 TCP 且禁用 HTTP。
必须显式区分:
- 若服务端只监听
9000:DSN 用tcp://127.0.0.1:9000?database=default&secure=false&compress=false - 若只开放
8123:DSN 改为http://127.0.0.1:8123?database=default&compress=true,且必须配Auth: clickhouse.Auth{Username:"default", Password:"xxx"}—— URL 中的user:pass在 HTTP 模式下会被忽略 - 别让驱动自己猜:显式设
Protocol: clickhouse.HTTP或Protocol: clickhouse.Native,否则默认 Native(TCP)会尝试连 8123 导致dial timeout
百万行 INSERT 崩内存?别拼 SQL,用 PrepareBatch 控批大小
单条 INSERT 或拼大字符串会导致 GC 飙升、OOM,甚至触发 ClickHouse 的 max_insert_block_size 限流。核心不是“怎么插”,而是“怎么分批”。
正确做法:
- 禁用
db.Query("INSERT ...")和循环调stmt.Exec() - 用
conn.PrepareBatch("INSERT INTO t (a,b) VALUES (?,?)"),每批塞10000–100000行 - 调
batch.Send()前复用batch.Reset(),避免反复 new 实例 - 数据来自文件或管道时,优先走
conn.Writer().Write()+bytes.Reader,底层缓冲复用,比 batch 快 3–5 倍
Scan() panic 或读出零值?Nullable 和时区必须显式处理
sql.NullString 不是可选,是刚需;time.Time 解析失败往往不是代码错,而是服务端和客户端时区没对齐。
关键动作:
- 连接后立刻执行
conn.Exec("SET timezone = 'Asia/Shanghai'"),或 DSN 加&timezone=Asia%2FShanghai -
Nullable(String)字段必须用sql.NullString或 v2 驱动的ch.String接收,直接用*string必 panic -
SELECT必须显式列字段,顺序要和Scan(&a, &b, &c)严格一致;别用SELECT * - 扫描循环结束后必须检查
if err := rows.Err(); err != nil—— 很多“空结果”其实是中途静默失败
查询卡死或超时频繁?context 和 max_execution_time 缺一不可
ClickHouse 查询不是越快越好,是越可控越好。没超时、没服务端限流,一个慢查询就能拖垮整个 goroutine。
每个查询都得带两层防护:
- Go 层:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second),传给conn.Query(ctx, ...) - ClickHouse 层:SQL 末尾加
SETTINGS max_execution_time = 20,防服务端无限执行 - WHERE 条件里慎用
time.Now()直接比DateTime字段 —— 时区不统一就查不到数据,建议转成带时区字符串再传
复杂点在于:这两层超时不能互相替代。context 超时是客户端断连,max_execution_time 是服务端主动中止。漏掉任一个,线上都可能出雪崩。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











