bigywater.tech
Scrapy · 49

请求积压:内存占用与 JOBDIR 的取舍

爬虫发现的链接比下载的速度快得多时,请求会堆在调度器里。内存队列每个请求占多少?meta 里的东西算不算?JOBDIR 能省多少内存、代价是什么?这篇用 5 万个积压请求量一遍。

≈ 1 KiB
内存队列里一个积压请求的内存(50,000 个增加 54.9 MiB)
0.2 MiB
同样 50,000 个请求用 JOBDIR 时进程 RSS 的增量
234 字节
JOBDIR 里一个请求落盘后的大小

内存队列

实测 · 本机执行输出
         N队列                    start() 耗时 s       积压    RSS 增量 MiB       每个请求约 KiB
     10000内存                            0.17     9999          10.3            1.05
     10000磁盘 JOBDIR                     0.22     9999           0.3            0.03
     50000内存                            0.91    49999          54.9            1.12
     50000磁盘 JOBDIR                     1.07    49999           0.2            0.00

默认队列把每个等待下载的请求都留在内存里,大小随请求数线性增长,meta 里放的东西也算在内。

磁盘队列

实测 · 本机执行输出
  无 meta:见上一节;带 1 KB meta:RSS 增量 120.1 MiB(每个请求约 2.46 KiB),start() 耗时 0.94 s

设了 JOBDIR 之后请求序列化到磁盘队列,进程内存几乎不再随积压增长,代价是序列化耗时(这里约 20%)和请求必须可 pickle。

什么时候要在意

实测 · 本机执行输出
  JOBDIR 总大小 11.1 MiB,每个请求约 234 字节;文件:0s=11412 KiB

上百万级积压(比如 start() 一次产出全部 URL)时内存队列会占 GB 级内存;此时改用 JOBDIR,或让 start() 按下载节奏分批产出。

分步演示

请求积压
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Scrapy 2.19.0 / Twisted 26.4.0 / lxml 6.1.3 / parsel 1.12.1(临时虚拟环境);所有请求只发给本机自己起的测试服务器(127.0.0.1,端口 9960 以上,不访问任何真实网站);服务器可以按实验需要制造延迟、重定向、压缩、断连等行为,并记录收到的原始请求;每个实验在临时目录里新建 Scrapy 项目或单文件 spider,命令输出来自真实的 scrapy 进程;计时用 time.perf_counter,数字每次运行会有小幅抖动。并发 1、服务器每个请求 0.5 s,爬虫 6 秒后被 CLOSESPIDER_TIMEOUT 关闭;RSS 增量取 start() 开始到结束 ru_maxrss 的变化(峰值,单位 macOS 字节);请求用 dont_filter 避免去重集合的内存干扰
    • 没有验证:百万级积压的真实内存;去重指纹集合的内存(见去重那一篇:每个指纹约 87 字节,另外计);DownloaderAwarePriorityQueue 多槽(多域名)时每槽的队列开销;磁盘队列读回(出队)的耗时;JOBDIR 下 SCHEDULER_MEMORY_QUEUE 与磁盘队列同时使用的比例
    京ICP备2026067502号-1京公网安备11010502063487号