scrapy pipeline 连 mongodb 分片集群需用 mongoclient 指向 mongos 地址并禁用 directconnection=true,初始化 client 于 init 中复用,分片键推荐 {"spider_name": 1, "hash_url": 1},写入用 insert_many(..., ordered=false) 控制批次 50–200 条。

Scrapy Pipeline 怎么连 MongoDB 分片集群
直接用 pymongo.MongoClient 连分片集群,和连普通副本集写法一样,但连接字符串必须带 replicaSet 参数或启用 directConnection=false(MongoDB 6.0+ 默认行为),否则会绕过 mongos 路由,直连 shard,导致写入失败或数据不一致。
常见错误现象:OperationFailure: not master 或 NotMasterError,本质是 Pipeline 误连了某个 shard 的 primary,而不是通过 mongos 写入。
- 连接字符串必须指向 mongos 地址,例如:
"mongodb://mongos1:27017,mongos2:27017/?replicaSet=rs0&authSource=admin"(注意:这里replicaSet指的是 mongos 所在的配置服务器副本集名,不是 shard 的) - 务必禁用
directConnection=true,否则 PyMongo 5+ 会跳过发现逻辑,直连第一个地址 —— 那很可能是个 shard 节点 - 推荐显式设置
minPoolSize=5和maxPoolSize=20,避免 Scrapy 并发高时频繁建连断连
Item Pipeline 里怎么维持长连接不每次新建 client
Scrapy 的 Pipeline 是单例对象,__init__ 只执行一次,所以 client 必须在这里初始化并复用,不能在 process_item 里反复 new MongoClient —— 否则每条 item 都新建连接池,很快耗尽系统文件描述符。
实操建议:
- 在 Pipeline
__init__中创建self.client = MongoClient(...),并调用self.db = self.client["mydb"]、self.collection = self.db["items"] - 不要在
close_spider里调用self.client.close()—— Scrapy 进程退出时自动释放;强行 close 反而可能中断正在写的批量操作 - 如果爬虫长期运行(>24h),可选加
serverSelectionTimeoutMS=5000和heartbeatFrequencyMS=10000,让 client 主动探测 mongos 存活性
分片键设计不当导致写入倾斜怎么办
即使连对了 mongos,如果 collection 没开分片,或分片键(shard key)选得差,所有数据会扎堆写进一个 chunk,吞吐卡死,CPU 在单个 shard 上飙高。
使用场景判断:
- Scrapy 抓取的 item 通常带
url、timestamp、spider_name字段 ——url天然离散但太长,不适合作 shard key;timestamp单调递增,绝对不能单独用;推荐组合如{"spider_name": 1, "hash_url": 1},其中hash_url是对url做hashlib.md5().hexdigest()[:8]截取 - 务必提前在 MongoDB 执行
sh.shardCollection("mydb.items", {"spider_name": 1, "hash_url": 1}),Pipeline 不负责建库建集合,也不该有权限 - 写入前检查
self.collection.options().get("shardKey"),如果为None,说明没分片,此时应抛异常或打 warning,而不是静默写入
批量写入时怎么避免阻塞 Scrapy 的异步流水线
collection.insert_many() 默认是同步阻塞的,如果网络抖动或 mongos 负载高,会拖慢整个 Scrapy 的 item 流水线,甚至触发 Twisted reactor 超时。
性能关键点:
- 不要逐条
insert_one—— 开销太大;改用insert_many(items, ordered=False),ordered=False允许跳过单条错误继续写,避免整批失败 - 控制批次大小:按经验,50–200 条/批较稳;太大易触发 mongos 内存限制(默认 16MB BSON 限制),太小则 RPC 开销占比高
- 不推荐用
asyncio或线程池包装写入 —— Scrapy 的 Pipeline 不是协程上下文,强行异步反而破坏调度逻辑;真要解耦,应把数据发到 Kafka 或 Redis,另起服务消费落库
容易被忽略的是:分片集群下 writeConcern 默认是 {w: 1},即只等 mongos 确认接收。如果业务要求强一致性,需显式设 w="majority",但这会明显拖慢写入延迟 —— 得自己权衡。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











