用生成器函数实现gb级数据流式分片迭代,核心是不加载全量到内存,通过逐行/按块读取文件、fetchmany查数据库、mmap处理二进制等方式对齐yield节奏,按需产出固定大小批次。

用生成器函数实现 GB 级数据的流式分片迭代,核心是“不加载全量到内存,边读边切、边产边用”。关键不在生成器语法本身,而在如何把数据源(如大文件、数据库游标、网络流)与 yield 节奏对齐,按需吐出固定大小的批次。
一、从大文件按行/按块分片:避免 readlines() 全载入
GB 文本文件(如日志、CSV)最常见。直接 f.readlines() 会 OOM;应逐行读取 + 批量 yield:
- 用
for line in f:迭代文件对象,内存只存单行 - 内部累积列表达指定数量(如 1000 行)后
yield batch,再清空继续 - 最后一组不足批量时,别漏掉
if batch: yield batch
示例片段:
def file_chunker(path, batch_size=1000):with open(path, encoding='utf-8') as f:
batch = []
for line in f:
batch.append(line.rstrip('\n'))
if len(batch) == batch_size:
yield batch
batch = []
if batch:
yield batch
二、处理二进制大文件:按字节块读取 + 解析边界
如大型 Protocol Buffer、自定义二进制格式,需控制每次读取字节数,但注意不能截断记录:
- 用
f.read(chunk_bytes)每次读固定字节(如 64KB),比逐行更底层 - 若记录有长度头(如前4字节是 payload 长度),需缓冲未完成记录,跨块拼接
- 推荐先用
mmap映射文件,再配合生成器按逻辑记录切片,更高效
三、对接数据库游标:用 fetchmany 替代 fetchall
查千万级表时,cursor.fetchall() 仍会把全部结果集加载进 Python 内存。正确方式:
- 执行查询后,用
cursor.fetchmany(size)循环取批,每次只拿一批(如 5000 行) - 每批
yield后,Python GC 可及时回收该批引用,内存保持恒定 - 注意:需确保数据库驱动支持服务器端游标(如 PostgreSQL 的
name参数,MySQL 的SSCursor),否则 fetchmany 仍是客户端缓存
四、组合与增强:添加进度、过滤、转换逻辑
真实场景常需在分片中嵌入处理链。生成器天然适合管道化:
- 用装饰器或嵌套生成器封装:一个生成器负责读取分片,另一个负责解析/清洗/类型转换
- 加简单进度提示:在每次
yield前打印当前偏移或批次序号(注意别影响性能) - 支持提前终止:调用方用
break退出 for 循环,生成器自动停止,底层文件/连接可被 finally 或上下文管理器安全关闭
不复杂但容易忽略。










