连不上 clickhouse?先检查协议和端口是否匹配:tcp 默认9000,http 默认8123;空查询需检查rows.err();百万插入用preparebatch而非单条query;分布式group by需加settings强制合并。

连不上 ClickHouse?先看协议和端口对不对
绝大多数连接失败不是密码错或网络不通,而是驱动默认协议和服务端暴露的端口不匹配。clickhouse-go(v2+)默认走 TCP 协议(9000 端口),但很多本地开发环境只开了 HTTP(8123),而云服务又可能只开 TCP 且禁用了 HTTP。
- 用
curl -v http://host:8123/测试 HTTP 是否可达;用telnet host 9000测试 TCP 是否监听 - 若只开放 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,并确认服务端<http_port></http_port>已启用 - v2 驱动默认开启 TLS 和 LZ4 压缩,服务端没配证书或压缩方式不一致时,会静默卡住或报
Code: 287. DB::Exception: Unknown compression method
Query 返回空 slice 却没报错?别跳过 rows.Err() 和 Columns()
ClickHouse 查询结果为空,常常不是数据不存在,而是 rows.Scan() 在中途出错却没被检查——driver 不会在扫描失败时自动返回 error,而是继续执行直到 rows.Next() 返回 false。
- 必须在
for rows.Next()循环结束后加if err := rows.Err(); err != nil { ... } - 别用
SELECT *,显式写出字段名,并严格按顺序传入Scan(&id, &name, &ts)的地址 - 调用
rows.Columns()检查字段名和类型,尤其当 SQL 含COUNT()、GROUP BY或别名时,列顺序可能和建表顺序不一致 - Nullable(String) 字段必须用
sql.NullString接收,直接用*string会 panic
INSERT 百万行慢如爬?放弃单条 Query,改用 PrepareBatch
ClickHouse 的写入性能严重依赖批量大小。单条 INSERT VALUES (...) 触发一次小合并(minor merge),完全违背列式存储设计初衷;拼接大 SQL 字符串则吃 CPU、占 GC、无法复用执行计划。
- 禁用
db.Query("INSERT ...")或stmt.Exec()传单行数据 - 用
conn.PrepareBatch("INSERT INTO table"),再循环batch.Append(...),最后batch.Send() - 每批控制在 10,000–100,000 行之间:太小(如 100 行)仍频繁交互;太大(如 500,000 行)易触发服务端
max_insert_block_size限制或客户端 OOM - 若数据来自流(如 CSV/JSON),优先用
conn.Writer().Write(),它底层缓冲 + 复用连接,比 batch 快 3–5 倍
GROUP BY 或分布式查询结果不准?加 SETTINGS 强制合并
在分布式表、JOIN 或多分片场景下,ClickHouse 默认可能跳过最终结果合并,导致 COUNT() 少算、ORDER BY 无序——这不是 Go client 的 bug,而是服务端优化开关生效了。
- 在 SQL 开头显式加上
SETTINGS distributed_group_by_no_merge = 0 - 如需全局生效,可在 DSN 中加
&settings.distributed_group_by_no_merge=0 - 类似地,
optimize_sorting_by_input = 0可避免 ORDER BY 被跳过 - 注意:这些 setting 仅对当前 query 生效,不能靠客户端缓存或连接级配置一劳永逸
最常被忽略的是:TCP 模式下不支持多行 INSERT VALUES (),(()) 语法,必须用 batch;而 HTTP 模式虽支持,但压缩和 TLS 配置稍有偏差就静默失败。协议、批量、空值、setting —— 四个点漏一个,查询就可能“看起来成功,实际错得离谱”。











