目标
任何内存占用随文件大小线性增长的变换,都精确地工作到文件超过机器容量那一天为止。流式管道让内存保持恒定:读一块、变换、推送、让背压自动调整整条链路的节奏。重写的工作量远小于它避免的那次深夜 OOM 事故,且抽样对比能证明行为与旧版完全一致。
步骤要点
- 先用旧版整文件读写测出当前内存天花板,记录 RSS 与输入成正比的量化证据。
- 用 stream/promises 的 pipeline 把读取、readline、Transform、写入串成管道,解析失败走 cb(err) 立即中止而不是静默跳行。
- 后台运行流式版本并每 30 秒采样 RSS,确认内存钉在几十 MB 不随文件增长。
- 取前一万行样本分别跑新旧两版,规范化排序后 diff,字节级一致才允许替换夜间作业。
- 给生产脚本加行数守卫:输入非空行数与输出行数不等就报错退出,杜绝截断文件还报成功。
排错提示
变换输出乱码是 chunk 边界切开了 UTF-8 序列,给读流设 encoding 按字符串处理或在 Transform 里包 StringDecoder;中途 EMFILE 是别处句柄太多,管道本身只开一个流,排查并发作业或调大 ulimit,错误时 pipeline 会负责关流;吞吐反而变差是逐行 JSON.parse 开销主导,改按 highWaterMark 批量攒行再解析推送,摊薄单次调用成本。
适用边界
按行流式的前提是记录有边界,任意二进制格式需先实现分帧逻辑;常量内存不等于零成本,背压会传播,总时长受最慢一环限制。
代码示例
whole-file version, 3GB input:
readFileSync -> 4.1GB RSS -> OOMKill (exit 137) at 3.2GB file
streaming pipeline, same input:
00:00 RSS 48MB
01:30 RSS 51MB
03:12 RSS 49MB (file finished, 3GB -> 2.7GB scrubbed)
row guard: in=41,923,004 out=41,923,004 -> OK