分块处理的核心是“不贪多”,需动态设定chunksize、预估内存占用、锁定数据类型、及时清理中间结果并避免累积拼接,推荐用生成器或流式工具实现恒定内存处理。

分块处理的核心是“不贪多”,每次只拿一小部分数据进来处理,处理完立刻释放,不让内存被长期占满。关键不在“分多少块”,而在于控制每块的大小、明确处理边界、及时清理中间结果。
根据内存余量动态设 chunksize
别凭感觉填 10000 或 50000。先用前 1000 行估算单行平均内存占用:
- 运行 pd.read_csv(filename, nrows=1000).memory_usage(deep=True).sum() // 1000,得到字节数/行(比如 78 KB)
- 查看系统空闲 RAM(如 1.2 GB),粗算理论上限:1.2 × 1024 × 1024 ÷ 78 ÷ 1024 ≈ 16000 行
- 保守起见,初始设 chunksize=5000,跑通后再逐步试到 10000 或 15000,同时观察 Python 进程的 RSS 内存变化
- 绝对不要留空或设为 None——那等于全量加载,失去分块意义
读取时就锁定类型和结构
每块独立推断类型会出错,还会拖慢速度:
- 用小样本跑一次 pd.read_csv(filename, nrows=10000).dtypes,把结果转成字典传给 dtype 参数,例如 {'id': 'uint32', 'status': 'category'}
- 时间列必须显式指定 parse_dates=['created_at'],不能依赖自动识别
- 含大量缺失的数值列,优先用 'Int64'(nullable int)或 'string' 类型,比默认 float64 省一半内存
处理完就落地,别堆在内存里
常见错误是把所有 chunk 收集起来再 concat,结果内存越积越多:
- 要写入新文件?用 to_csv(..., mode='a', header=False) 追加写,每块处理完即丢弃
- 要做聚合统计?每块内先算局部结果(如 chunk.groupby('cat')['val'].sum()),再合并局部结果,最后统一收口
- 严禁写成 df = pd.concat([df, chunk]) 循环拼接——引用链不断变长,就是典型的内存泄漏
配合生成器或异步流进一步减压
当数据源是文件流、API 分页接口或数据库游标时,分块只是起点:
- 用 Python 生成器表达式 (process(row) for row in large_file) 实现逐行惰性处理,内存恒定
- C# 中可用 IAsyncEnumerable
+ await foreach 处理网络或大文件流,天然支持背压 - Data Formulator、Excel Agent 等工具内置流式清洗模块,直接启用“流式处理”开关即可跳过全量加载环节










