GridFS默认chunkSize为256KB,仅支持约16MB/批写入,无法充分利用千兆网卡带宽;建议调至1MB或4MB,并同步优化驱动版本、maxWriteBatchSize、Nginx缓冲及分片策略。

GridFS默认chunkSize太小,限制了单次批量写入吞吐
千兆网卡理论带宽约125MB/s,但GridFS默认chunkSizeBytes: 262144(256KB),MongoDB驱动一次最多批量写入约64个chunk——也就是约16MB/批。这导致网络层频繁发包、TCP握手开销大、无法持续喂饱网卡。实测中,即使后端IOPS充足,单连接吞吐也常卡在30–60MB/s。
实操建议:
- 将
chunkSizeBytes调至1048576(1MB)或4194304(4MB),让单次批量写更接近网卡MTU窗口上限 - 确认驱动支持:PyMongo ≥ 4.0、Node.js MongoDB Driver ≥ 6.0 才能稳定处理 >1MB chunk
- 调大后必须同步检查
maxWriteBatchSize(mongod日志里搜write batch size),避免因批次超限自动降级为逐条写
GridFS下载流不背压,但客户端没及时消费导致发送阻塞
很多人以为“网卡没跑满”是服务端发得慢,其实是下游消费跟不上:比如Nginx proxy_buffering on、应用层read(64*1024)太小、或前端HTTP/1.1无流控接收。mongod拼命推数据,但socket send buffer积压后内核会降低TCP窗口,最终倒逼mongod减缓发送节奏。
常见错误现象:
-
iftop -P 27017看到连接长期维持在10–20MB/s,但ss -i显示wscale从8降到0、retrans上升 - Nginx access log里大量
206 Partial Content响应,但upstream_response_time波动剧烈
实操建议:
- Nginx反代GridFS时,必须设
proxy_buffering off+proxy_buffer_size 4k,避免缓冲区吃掉背压信号 - 应用层用
gridfs_stream.read(1024*1024)(1MB)而非64KB,减少系统调用频次 - 若用HTTP/1.1,加
Connection: keep-alive和Transfer-Encoding: chunked,确保流式传递不中断
分片集群下跨shard流量未压缩或被路由放大
即使单机mongod能跑满千兆,分片环境下仍可能受限:GridFS的files和chunks集合若未按_id分片,所有写入都打到primary shard;或者moveChunk过程中,snappy压缩只作用于shard-to-shard内部流量,而客户端→mongos→shard路径仍是明文BSON,带宽直接翻倍。
实操建议:
- 确认
fs.files和fs.chunks已启用分片:sh.status()中对应集合enabled为true - 用
{ _id: "hashed" }分片键,避免热点集中;上传前生成业务ID作_id,保证局部性 - 检查
mongos与shard间是否同机房部署——跨机房场景下,哪怕开了snappy,元数据同步和查询路由本身也会吃掉可观带宽
客户端驱动或语言运行时存在隐式缓冲瓶颈
Python的io.BufferedIOBase、Node.js的Readable流、Java的GridFSDownloadStream都自带缓冲层。当底层socket发送快于应用层处理速度时,这些缓冲会先吃掉数据,再以固定节奏吐出,人为制造“带宽上不去”的假象。
容易被忽略的关键点:
- PyMongo返回的
GridFSDownloadStream是io.RawIOBase子类,**不自动缓冲**——但如果你用iter(lambda: stream.read(8192), b''),就等于手动加了8KB缓冲,且无法控制节拍 - Node.js中
bucket.openDownloadStream().pipe(res)看似流式,但如果res是ExpressResponse对象,它内部会做write(chunk)合并,实际吞吐取决于Node.js事件循环调度频率 - Go driver默认
Read缓冲为32KB,需显式调SetReadBufferSize(1024*1024)才能匹配千兆网卡节奏
真正卡住千兆带宽释放的,往往不是某一个参数,而是chunkSize、分片策略、缓冲行为三者没对齐——比如你把chunkSize调到4MB,但Nginx还开着proxy_buffering,或者分片没开,所有流量挤在一台shard上,CPU先跑满了。











