必须用clickhouse-go/v2的tcp连接+preparebatch写入+replacingmergetree表结构,否则日志写入延迟高、查不准、oom频发;连接失败需核对协议、端口、secure和compress配置是否匹配服务端实际监听方式。

直接用 clickhouse-go/v2 的 TCP 连接 + PrepareBatch 写入 + ReplacingMergeTree 表结构,否则日志写入延迟高、查不准、OOM 频发。
连接失败?先核对协议、端口、secure 和 compress 三者是否对齐
本地开发连不上,90% 是因为驱动默认走 TLS+LZ4 的 TCP(9000 端口),但你的服务只开了 HTTP(8123)或裸 TCP 未配证书。错误常表现为 context deadline exceeded,不报具体原因。
- 确认 ClickHouse 实际监听的端口:
telnet 127.0.0.1 9000或curl -v http://localhost:8123/ - 若只开 9000(TCP):DSN 必须是
tcp://127.0.0.1:9000?database=default&secure=false&compress=false - 若只开 8123(HTTP):DSN 改为
http://127.0.0.1:8123?database=default&compress=true,并确保服务端http_port已启用 -
Addr必须是[]string切片,写成单个字符串会编译失败;Options.Auth.Username默认是"default",密码为空也要显式传空字符串
写入百万级日志慢?别用 Query 或 Exec,必须走 PrepareBatch
单条 INSERT 触发高频小块写入,MergeTree 持续 minor merge,CPU 和磁盘 IO 直线飙升。实测 10 万行用 stmt.Exec() 耗时 30s+,改用 batch.Send() 后压到 800ms 内。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 调用
conn.PrepareBatch("INSERT INTO logs (...) VALUES (?, ?, ?)"),不是conn.Query -
batch.Append()只是内存攒数据,漏掉batch.Send()就等于没发 - 每批控制在 10000–100000 行之间:太小(如 500 行)仍频繁交互;太大(如 50 万)易被服务端
max_insert_block_size拒绝或客户端 GC 压力大 - 别传 struct 实例:
batch.Append(myStruct)会 panic;必须按列拆成[]int64、[]string等切片再分别Append()
查日志总为空或字段乱码?检查 rows.Err()、Nullable 类型和时区
查询返回空 slice 不代表没数据,很可能是扫描中途出错但被忽略;ClickHouse 的 Nullable(String) 和 Go 的 *string 完全不兼容;时间字段跨时区会导致 WHERE 失效。
- 循环
rows.Next()后必须加if err := rows.Err(); err != nil { ... },否则静默失败 -
SELECT user_id, message中message是Nullable(String),接收变量必须是sql.NullString或ch.String(需import "github.com/ClickHouse/clickhouse-go/v2/lib/columns") - 时间字段统一用
time.Time,写入前转 UTC:ts.UTC();查询时避免WHERE event_time > '2026-06-30 12:00:00',改用WHERE event_time >= toDateTime('2026-06-30 12:00:00', 'UTC') - 结构体字段打
chtag 时大小写、下划线必须和表字段完全一致:UserID int64 `ch:"user_id"`,写成`ch:"User_id"`或漏 tag,字段就永远是零值
表设计不匹配导致 GROUP BY 结果不准?用 ReplacingMergeTree + 按天分区 + 复合排序键
行为日志常有重复上报或延迟到达,用普通 MergeTree 会累积脏数据;不分区或排序键不合理,会导致 GROUP BY 在分布式节点上无法合并,结果少算。
- 建表必须用
ReplacingMergeTree,主键含(event_date, user_id, event_type, event_time),靠version字段去重 -
PARTITION BY toYYYYMMDD(event_time),按天分区,避免单分区过大影响查询和后台 merge - 排序键首字段必须是分区字段(如
event_date),否则WHERE event_date = '2026-06-30'无法剪枝 - 分布式环境执行
GROUP BY时,在 SQL 末尾加SETTINGS distributed_group_by_no_merge=0强制汇总
最易被忽略的是 batch.Send() 调用时机和 rows.Err() 检查——前者漏了等于白写,后者漏了等于白查;还有就是 ch tag 拼错或大小写不一致,结构体字段永远收不到值,还不会报错。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










