ClickHouse千万级数据插入必须采用Native协议+列式块写入+分片对齐+合理缓冲,禁用VALUES行模式;关键设置为insert_block_size=100000、use_numpy=True、compress=True,并强制columnar=True。

直接用 client.execute 逐行或小批量插入千万级以上数据,基本等于主动触发性能雪崩。真正可行的路径是:走 Native 协议 + 列式块写入 + 分片对齐 + 设置合理缓冲,缺一不可。
为什么不能用 VALUES 行模式插入大表
行模式(即默认的 client.execute("INSERT INTO t VALUES", data))在数据量超过几万行时就会暴露严重瓶颈:
- 每批次实际只打包几十到几百行,网络往返开销远超数据本身
- ClickHouse 服务端需为每一行做类型推导和内存拷贝,
use_numpy=False时 CPU 消耗陡增 - 遇到
DateTime或Nullable字段时,Pythondatetime/None转换成本被放大数倍 - 集群环境下,分布式表若未显式指定
sharding_key,写入会随机打散,引发跨节点 shuffle 和写放大
必须开启的三项 client 设置
不改这三项,其他优化全白搭:
-
insert_block_size:设为100000(非默认的1048576)。过大易 OOM,过小无法触发底层向量化写入;实测50000–150000是 SSD+10G 网络下的甜点区间 -
use_numpy:强制设为True。数值型字段(UInt64,Float64,Decimal)能跳过 Python 对象循环,直通 C 层序列化 -
compress:设为True。Native 协议支持 LZ4 压缩,千兆网下压缩比常达 3:1,显著降低传输时间
构造 client 示例:
client = Client(<br> host='ch-proxy', # 推荐走负载均衡代理,而非直连某节点<br> port=9000,<br> user='writer',<br> password='xxx',<br> settings={<br> 'insert_block_size': 100000,<br> 'use_numpy': True,<br> 'compress': True<br> }<br>)
立即学习“Python免费学习笔记(深入)”;
列模式(columnar=True)不是可选项,是必选项
行模式传的是 [(v1,v2,v3), (v1,v2,v3), ...],列模式传的是 [col1_list, col2_list, col3_list] —— 后者与 ClickHouse 底层存储格式完全对齐:
- 避免服务端重新切分列,减少一次完整内存拷贝
- NumPy 数组可直接 mmap 到 wire protocol buffer,零拷贝序列化
- 对含
Nested或Array字段的表,列模式是唯一稳定支持的写入方式
正确写法:
# data 是 list of lists,每子列表是一列<br>client.execute(<br> "INSERT INTO events (ts, user_id, event_type) VALUES",<br> data,<br> columnar=True # 必须显式声明<br>)
写入前必须确认的三件事
很多“写入慢”问题其实卡在元数据层,而非传输或解析:
- 目标表引擎是否为
MergeTree系或其变体(如ReplacingMergeTree)?TinyLog或Memory引擎无法支撑持续写入 - 本地表(非分布式表)的
PARTITION BY表达式是否匹配数据时间范围?错配会导致写入被路由到错误分区,甚至静默失败 - 若走分布式表,确认建表 DDL 中是否包含
sharding_key(如intHash32(user_id)),且客户端写入时未覆盖该字段值 —— 否则数据会乱序分布,后续查不出
最后提醒一句:所有优化都建立在“单次写入至少 5 万行”基础上。如果业务逻辑天然只能攒 200 行就 flush,那不如直接换 Kafka + MaterializedMySQL 链路,硬压 Native 协议只会放大延迟抖动。


















