唯一可靠解法是分块读取+流式处理,须配合dtype、usecols和引用清理;chunksize需按样本内存反推,textfilereader是迭代器不可直接调用dataframe方法,循环中严禁累积chunk。

直接用 pandas.read_csv() 读超大 CSV 几乎必然触发 MemoryError,核心问题不是文件太大,而是它默认全量加载 + 自动 dtype 推断,导致内存占用常达原始文件的 3–5 倍。唯一可靠解法是分块读取 + 流式处理,且必须配合 dtype、usecols 和引用清理。
chunksize 不是设了就省内存,得先算单块实际内存
很多人设 chunksize=10000 就以为安全,结果第 3 块就爆内存——因为 chunksize 只控行数,不控内存。一行含长文本的 object 列,比 100 行 int32 还吃内存。
- 先试读小样本:
df_sample = pd.read_csv("big.csv", nrows=5000) - 算真实内存:
df_sample.memory_usage(deep=True).sum() / 1024**2(单位 MB) - 按目标内存反推:若样本占 80MB,你机器可用 600MB,则合理
chunksize ≈ 600 / 80 * 5000 = 37500 - 特别注意高基数列(如日志中的
user_agent)、稀疏列(大量NaN),它们会让单块内存暴涨
TextFileReader 不是 DataFrame,别直接调 .shape 或 .head()
pd.read_csv(..., chunksize=N) 返回的是 TextFileReader 对象,本质是迭代器。常见错误是把它当 DataFrame 用,轻则报错,重则漏掉首块数据。
- 错误写法:
reader = pd.read_csv("x.csv", chunksize=5000); print(reader.shape)→AttributeError - 正确预览第一块:
first_chunk = next(pd.read_csv("x.csv", chunksize=5000)),但注意这会消耗迭代器第一个元素 - 真要循环处理:
for chunk in pd.read_csv("x.csv", chunksize=5000): process(chunk),别提前赋值给变量再循环
循环里千万别攒 chunk,处理完立刻丢引用
最常踩的坑是把每个 chunk .append() 进列表,或反复 pd.concat() 累积——这等于在内存里重建全量数据,GC 来不及回收,第 4–6 块后必崩。
- 流式写 CSV:
chunk.to_csv("out.csv", mode="a", header=first_write),首次header=True,之后mode="a" - 流式入库:
chunk.to_sql("table", con, if_exists="append", index=False),确保con是带连接池的 SQLAlchemy 引擎 - 做全局统计(如总和):
total += chunk["val"].sum(),只留标量,别存chunk - 显式释放:
del chunk+import gc; gc.collect()(大文件下建议加)
dtype 和 usecols 不是可选项,是保命项
不指定 dtype,pandas 会把整数推成 int64、字符串塞进 object;不加 usecols,300 列日志你只用 3 列,却仍加载全部——这两项不做,chunksize 再小也白搭。
- 分类列省内存最多:
dtype={"status": "category", "country": "category"}(可降 70%+) - 数值列降精度:
"score": "float32"(减半)、"id": "uint32"(避免默认int64) - 跳过无用列:
usecols=["user_id", "event_time", "action"](比读完再drop省 30%+ 内存) - 禁用索引:
index_col=False(除非真需要行号)
真正难的不是“怎么分块”,而是每块处理逻辑必须闭环:过滤、聚合、写磁盘/数据库,全在 for 循环体内完成。一旦离开循环,chunk 就该被销毁——任何依赖它后续补操作的想法,都会让前面所有优化归零。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











