ETL 脚本处理 200 万行时 RSS 1.2G,到 800 万行涨到 6G,被 OOM killer 杀掉。代码里没看到明显的大对象。乍看像缓存或者权限的锅,追下去往往不是。
import tracemalloc
tracemalloc.start()
snap = tracemalloc.take_snapshot()
for stat in snap.statistics('lineno')[:3]: print(stat)
追下去是这么回事:脚本把整个结果集 fetchall 到列表,再逐行转换追加到另一个列表。两份全量数据同时在内存里,行数翻倍内存就翻倍。转换过程还生成了大量临时字符串,垃圾回收跟不上分配速度。
碰到「ETL 内存增长」这类问题,我习惯先把当前配置原样备份一份,改坏了立刻换回来,不用现场推。验证用真实数据走一遍,测试数据太干净,边界情况根本碰不到。
能分两步做的操作就别合成一步,中间看一眼返回值,出问题好判断。报错里带路径的,先去看那个路径在不在、权限对不对。
具体做法是,游标改成流式读取 用 fetchmany 每批 5000 行;转换改写成生成器 处理完一批就写出去;别在循环里不断 append 到全局列表;用 tracemalloc 对比两批之间的增量确认增长点。
验收就看一点,同样 800 万行跑完 RSS 稳定在 900M 上下 不再随行数上涨。
