You need to enable JavaScript to run this app. 从ClickHouse取数到Pandas:降低内存占用及分块读取异常排查
优惠活动
大模型
产品
解决方案
定价
更多

从ClickHouse取数到Pandas:降低内存占用及分块读取异常排查

ClickHouse拉取数据至Pandas的内存占用异常说明与优化

实测场景

本次测试针对总计712300行的ClickHouse查询结果,分别测试三种读取方案的内存表现,所有内存数值均通过memory-profiler工具采集:

  • 全量读取方案(SQLAlchemy连接 + pd.read_sql无分块):最终内存占用754.0 MiB,总耗时1分8秒
  • 分块读取方案(SQLAlchemy连接 + pd.read_sql设置chunksize=100_000):最终内存占用813.1 MiB,总耗时1分12秒
  • Pandahouse库原生读取方案:最终内存占用290.1 MiB

分块读取内存更高的结果完全符合预期

核心原因

当前的分块实现没有真正实现内存优化的目标:你在迭代过程中把所有分块数据全部通过df.append(chunk)拼接到了同一个最终DataFrame中,最终内存中存储的内容和全量读取没有区别,反而因为Pandasappend方法的实现逻辑,引入了额外的内存开销。
具体的额外开销来自两点:

  1. append的临时拷贝开销:Pandas的append方法不会在原有DataFrame的内存位置上直接修改,每一次拼接操作都会重新生成一个新的DataFrame对象,拼接过程中会短暂同时存在旧的DataFrame和新的chunk数据的两份内存拷贝。从测试日志也能验证:光初始化分块迭代器阶段,内存就从89.4MiB涨到了661.8MiB(这部分是SQLAlchemy通道预加载的结果集缓冲区开销),后续8次append循环又带来了累计109.9MiB的增量,最终峰值813.1MiB,比全量读取的754.0MiB高出的部分,就是整个流程的临时拷贝带来的额外消耗。
  2. 对分块机制的认知偏差:pd.read_sql的chunksize参数的核心作用,是避免数据库一次性把全量结果集推送到客户端内存,适配超大结果集的场景。如果最终目标是把全量数据整合为一个完整的DataFrame,分块拼合的内存占用永远会高于直接全量读取的模式,不可能实现内存优化。

分块读取的正确低内存使用方式

只有当你不需要把全量数据一次性保留在内存中时,分块读取才能发挥降内存的作用:比如逐块做聚合统计、逐块写入持久化存储、逐块做数据清洗,每处理完一个chunk就直接丢弃,全程只在内存中保留当前正在处理的单个分块数据,此时峰值内存只会等于单个分块的大小(对应10万行数据大概几十MiB),完全不会涨到全量数据对应的内存水平。

不同读取方案的内存差异原因

Pandahouse方案的内存占用远低于SQLAlchemy通道的pd.read_sql,本质是底层数据传输与解析逻辑的差异:

  • SQLAlchemy连接走通用Python DBAPI通道:拉取的查询结果会先转换为Python原生的整数、字符串、浮点对象再交给Pandas构建DataFrame,这类Python原生对象的内存开销是Pandas列式存储的3-5倍,因此整体内存占用居高不下。
  • Pandahouse走ClickHouse原生HTTP协议:直接以列式格式拉取查询结果,跳过了Python原生对象中转的环节,直接在内存中构建Pandas的列式存储结构,因此内存占用会低很多。

内容的提问来源于stack exchange,提问作者Альберт Александров

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.31 06:27:01