关于 io-bound-data-processing
面向在单台受限设备上处理超出内存的数据集的工程师,提供数据处理参考。内容涵盖内存管理、I/O 模式、格式、分批、磁盘溢出、背压、并发与测量,并按执行影响排序。
主要功能
- 01流式与有界内存处理
- 02顺序及缓冲 I/O 模式
- 03格式与编码选择
- 04分块、批处理与磁盘溢出
- 05背压与并发建议
- 06吞吐量与瓶颈测量
适用场景
- 01避免处理超内存数据集时 OOM
- 02加速缓慢的文件或网络流水线
- 03按内存预算选择分块大小
- 04诊断小型设备上的 I/O 瓶颈任务
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pproenca/ dot-skills --skill io-bound-data-processing安装后在 Claude Code 中输入/io-bound-data-processing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
AGENTS.mdmetadata.jsonassets/templates/_template.mdreferences/_sections.mdbatch-coalesce-writes-with-buffered-output.mdbatch-keyset-pagination-over-offset.mdbatch-pick-chunk-size-by-memory-budget.mdbatch-process-with-stable-iterators.mdbatch-use-vectorized-apis-not-row-loops.mdcodec-dictionary-encoding-for-repetitive-strings.mdcodec-prefer-binary-protocols-over-json-for-rpc.mdcodec-train-a-zstd-dictionary-for-many-small-payloads.mdcodec-zstd-or-lz4-as-defaults-not-gzip.mdconc-asyncio-for-many-network-streams-not-for-cpu.mdconc-overlap-compute-with-prefetch.mdconc-thread-pools-for-blocking-io-libraries.mdconc-tune-parallelism-to-the-bottleneck.mdfmt-avoid-deeply-nested-json-for-hot-paths.mdfmt-columnar-for-analytical-scans.mdfmt-line-delimited-for-streaming-row-ingest.mdfmt-prefer-schema-on-write-when-possible.mdfmt-push-predicates-into-the-reader.mdio-async-for-many-concurrent-streams.mdio-batch-and-pipeline-network-roundtrips.mdio-buffer-explicitly-for-small-records.mdio-mmap-for-random-or-shared-large-files.mdio-prefer-sequential-over-random.mdio-stream-http-bodies-with-iter-content.mdio-zero-copy-when-moving-bytes-as-is.mdmem-bound-the-working-set.md详细介绍
受限资源下 I/O 密集型数据处理最佳实践
为工程师提供的参考指南,适用于在单台低算力设备上处理大于 RAM 的数据集。按照对执行生命周期的影响排序:表格顶部的规则决定任务能否运行;接近底部的规则则能再提升最后 10% 的性能。请从优化优先级最高的规则开始。
**范围:**适用于笔记本电脑、双 vCPU 容器或 Raspberry Pi 级别设备上的实际 ETL、数据工程和批处理工作的常见模式——流式处理、格式、分块、溢写、背压、编解码器,以及真正适合 I/O 密集型瓶颈的并发模型。不在范围内(其他地方已有介绍):算法原语本身(参见 computer-science-algorithms)、单台设备以外的分布式计算(请使用 Spark/Dask)以及数据库引擎内部机制(参见官方文档)。
内容提炼自 Apache Arrow / Parquet 文档、Polars 用户指南、DuckDB 文档、pandas — 扩展到大型数据集、Linux 手册页(mmap(2)、sendfile(2)、posix_fadvise(2))、Brendan Gregg 的 USE 方法和 Systems Performance、Kleppmann 的 Designing Data-Intensive Applications,以及 zstd / lz4 基准测试参考资料。
适用场景
遇到以下情况时,请参考这些规则:
- 任务因 OOM 被终止、使用交换空间,或在小型设备上运行速度远低于预期
- 输入数据大于 RAM,且需要扫描、筛选、聚合、排序或连接数据
- 管道各阶段之间存在无界缓冲区,或者「流式」任务中的内存持续线性增长
- 发现每行数据都要进行一次往返写入(每行执行一次
INSERT、每个 URL 执行一次requests.get、每条记录执行一次f.read(32)) - 正在选择格式、编解码器或序列化器,且该选择在大规模处理时会产生显著影响
top显示 CPU 使用率低、iowait 高,或者不清楚是哪种情况- 「速度很慢,但不知道为什么」——从
obs-类别开始排查
按优先级划分的规则类别
| # | 类别 | 前缀 | 影响 | 连锁影响的原因 |
|---|---|---|---|---|
| 1 | 内存管理 | mem- | 严重 | 在受限设备上将所有数据读入 RAM,会抵消后续所有优化手段的作用 |
| 2 | I/O 访问模式 | io- | 严重 | 磁盘/网络比 RAM 慢 10³–10⁶ 倍;访问模式决定了整体耗时 |
| 3 | 数据格式与编码 | fmt- | 高 | 在执行任何逻辑之前,格式就决定了 I/O 量和解码开销的下限 |
| 4 | 分块与批处理 | batch- | 高 | 粒度决定峰值内存,并摊薄每项开销 |
| 5 | 磁盘溢写与外部内存 | spill- | 高 | 数据大于 RAM 时,选择只有「妥善溢写」或「内存溢出」 |
| 6 | 流水线与背压 | pipe- | 中高 | 快速生产者与缓慢接收端之间的无界缓冲区会导致内存溢出 |
| 7 | 压缩与序列化 | codec- | 中 | 以 CPU 换取 I/O;选对编解码器可带来数量级的提升 |
| 8 | I/O 密集型工作负载的并发 | conc- | 中 | 异步、线程和进程是不同的工具;选错模型会浪费 CPU |
| 9 | 可观测性与吞吐量调优 | obs- | 低至中 | 无法调优未测量的指标;iowait 高不等于 CPU 密集型 |
快速参考
1. 内存管理规范(关键)
mem-stream-dont-slurp— 逐块遍历数据源;峰值 RAM 取决于块大小,而非文件大小mem-prefer-generators-over-lists-for-pipelines— 生成器可流式传递数据;列表会将数据全部加载到内存中mem-shrink-dtypes-before-loading— 使用更窄的整数类型和分类类型;加载时可将内存占用降低 2-8 倍mem-use-views-not-copies— 切片时不复制;利用 NumPy/Arrow 的零拷贝语义mem-bound-the-working-set— 块大小 = 预算 ÷ 行大小 × 放大系数,而不是随意取个整数mem-release-references-explicitly— 丢弃中间数据,避免峰值内存达到 N × 块大小
2. I/O 访问模式(关键)
io-prefer-sequential-over-random— 对偏移量排序,向内核提供建议,并利用预读io-buffer-explicitly-for-small-records—BufferedReader可将系统调用次数减少 1000 倍io-stream-http-bodies-with-iter-content— 使用stream=True和iter_content,而非.contentio-mmap-for-random-or-shared-large-files— 随机访问时使用零拷贝和按需分页io-async-for-many-concurrent-streams— 一个线程即可处理数千个 awaitio-batch-and-pipeline-network-roundtrips— 使用COPY、管道、多键端点和 HTTP/2io-zero-copy-when-moving-bytes-as-is— 使用sendfile、copy_file_range、shutil.copyfile
3. 数据格式与编码(高优先级)
fmt-columnar-for-analytical-scans— 对筛选和投影工作负载使用 Parquet/Arrowfmt-line-delimited-for-streaming-row-ingest— 流式处理时使用 NDJSON,而非 JSON 数组fmt-push-predicates-into-the-reader— 利用行组统计信息跳过整个数据块fmt-prefer-schema-on-write-when-possible— 在可能时优先采用写入时定义的模式;带类型的列始终优于读取时定义的模式fmt-avoid-deeply-nested-json-for-hot-paths— 热路径应使用扁平模式,或采用二进制格式
4. 分块与批处理(高优先级)
batch-pick-chunk-size-by-memory-budget— 根据内存预算计算块大小,而非使用固定值batch-use-vectorized-apis-not-row-loops— 使用 NumPy / Polars / Arrow 内核,而非iterrowsbatch-process-with-stable-iterators— 使用iter_batches、chunksize=、collect(streaming=True)batch-coalesce-writes-with-buffered-output— 使用COPY/executemany和合适大小的写入缓冲区batch-keyset-pagination-over-offset— 使用WHERE id > $last_id,深层游标上绝不使用OFFSET N
5. 磁盘溢写与外部内存(高)
spill-external-merge-sort-when-data-exceeds-ram— 外存排序;可行时委托给sort/ DuckDBspill-partition-by-hash-for-out-of-core-groupby-join— 对两侧进行哈希分区;逐个处理分区spill-use-temp-files-not-process-memory— 使用SpooledTemporaryFile,绝不使用无界的BytesIOspill-use-engines-that-spill-automatically— DuckDB / Polars / Dask 会自动为你管理磁盘溢写
6. 流水线与背压(中高)
pipe-use-bounded-queues-for-producer-consumer— 有界Queue是实现背压的机制pipe-apply-backpressure-from-slow-stages— 速度较慢的接收端会限制较快的源pipe-prefer-pull-iteration-over-push-callbacks— 拉取可自然实现背压;推送则需要策略pipe-checkpoint-progress-for-resumability— 每个批次后进行原子检查点;重做范围有限
7. 压缩与序列化(中)
codec-zstd-or-lz4-as-defaults-not-gzip— 根据访问模式选择编解码器;gzip 已过时codec-dictionary-encoding-for-repetitive-strings— 在低基数列上可实现 5-50 倍提升codec-prefer-binary-protocols-over-json-for-rpc— 热路径传输采用 Protobuf / Arrow IPC / MsgPackcodec-train-a-zstd-dictionary-for-many-small-payloads— 对小于 1 KB 的消息使用zstd --train
8. I/O 密集型工作负载的并发处理(中)
conc-asyncio-for-many-network-streams-not-for-cpu— Asyncio 可复用等待过程;对计算毫无帮助conc-thread-pools-for-blocking-io-libraries— 阻塞 I/O 期间 GIL 会释放;线程因此有效conc-overlap-compute-with-prefetch— 通过后台线程 / 协程提前预取一个批次conc-tune-parallelism-to-the-bottleneck— 根据受限资源调整工作线程数量
9. 可观测性与吞吐量调优(低至中)
obs-measure-iowait-not-just-cpu— 使用iostat -x、vmstat、psutil 找出真正的瓶颈obs-instrument-throughput-rows-per-second— 速率可统一不同运行的衡量标准;及时发现性能回退obs-profile-with-py-spy-or-strace-for-syscall-storms— 直接附加分析,不要靠猜
使用方法
先从与问题相符的问题开始:
- “流式作业发生 OOM/RAM 持续线性增长” →
mem-和pipe-(可能是缓冲区无界增长或每次迭代都在累积) - “磁盘占用率达到 100 %,但 CPU 处于空闲状态” →
io-(可能是随机访问、未缓冲的 I/O 或格式不合适) - “为什么读取这个 10 GB 文件这么慢?” → 先看
fmt-columnar-for-analytical-scans和io-buffer-explicitly-for-small-records - “分块大小应该是多少?” →
batch-pick-chunk-size-by-memory-budget - “数据无法全部装入 RAM” →
spill-(并优先选择能自动处理溢出的引擎:spill-use-engines-that-spill-automatically) - “有大量小型网络调用” →
io-batch-and-pipeline-network-roundtrips、io-async-for-many-concurrent-streams - “增加工作线程也没用” →
conc-tune-parallelism-to-the-bottleneck、obs-measure-iowait-not-just-cpu - “速度很慢,但我不知道原因” → 先看
obs-;先分析性能,再做任何更改
代码示例使用 Python(对不同受众来说最易读)。其原理具有通用性——其他生态系统中的同类库(Arrow C++/Rust/Java、Polars Rust、DuckDB 等,以及 libuv 风格的异步处理)遵循相同模式。
参考文件
| 文件 | 说明 |
|---|---|
| references/_sections.md | 分类定义与顺序 |
| assets/templates/_template.md | 添加新规则的模板 |
| metadata.json | 版本和参考信息 |
| AGENTS.md | 自动生成的目录导航 |
相关技能
computer-science-algorithms— 此技能所依赖的算法基础(外部归并排序、草图、哈希分区、抽样)