Mongos负载不均主因是客户端连接复用和分片键设计缺陷;应用应通过L4负载均衡(如HAProxy TCP模式)统一接入,避免驱动差异,并优化分片键以实现数据层均匀分布。

Mongos节点之间为什么会出现负载不均?
Mongos 本身不存储数据,只做路由和聚合,但它的负载不均往往不是因为“转发逻辑出错”,而是由客户端连接行为和分片键设计共同导致的。常见现象是:某个 mongos 实例 CPU 持续高于其他节点,netstat 显示大量 ESTABLISHED 连接集中在它身上,而其他 mongos 连接数很低。
- 客户端(如 Node.js 的
mongodb驱动)默认启用连接池,且首次连接后会复用连接,不会主动轮询多个mongos地址 - 应用启动时若只配置了一个
mongos地址(或 DNS 解析固定到某台),后续所有请求都会打到那台 - 即使配置了多个
mongos地址(如mongodb://mongos1:27017,mongos2:27017),驱动也仅在初始化时做一次 SRV 或 TXT 记录发现,并不持续感知节点健康状态
Load Balancer 直接代理 mongos 是否可行?
可以,但必须满足两个前提:TCP 层透传、不干预 MongoDB 协议内容。HTTP 负载均衡器(如 Nginx 默认 HTTP 模式)会直接报错,因为 MongoDB 使用的是二进制协议(BSON over TCP),不是 HTTP。
- ✅ 推荐用 L4(TCP)模式:HAProxy、AWS NLB、阿里云 TCP 类型 SLB
- ❌ 避免 L7(HTTP/HTTPS)模式:Nginx 默认
http块、Cloudflare、任何尝试解析或重写请求体的中间件 - ⚠️ 注意粘性(sticky session):MongoDB 客户端连接有上下文(如事务、会话 ID),LB 不应基于 IP 做长连接绑定,否则可能破坏分片路由一致性;应关闭
balance-on-connection类选项,让 LB 纯粹做无状态转发
示例 HAProxy 配置片段(关键部分):
frontend mongo_lb
bind *:27017
mode tcp
default_backend mongos_nodes
<p>backend mongos_nodes
mode tcp
balance roundrobin
option tcp-check
tcp-check send \x61\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xd4\x07\x00\x00\x00\x00\x00\x00\x6d\x6f\x6e\x67\x6f\x64\x62\x2e\x61\x64\x6d\x69\x6e\x2e\x24\x63\x6d\x64\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00
tcp-check expect string "isdbgrid"
server mongos1 10.0.1.10:27017 check
server mongos2 10.0.1.11:27017 check</p>应用层该不该自己做 mongos 轮询?
不推荐。多数官方驱动已支持多 mongos 地址列表,但行为因语言和版本差异大:
- Python PyMongo:从 3.12+ 开始支持自动故障转移和延迟感知(需开启
loadBalanced=True参数),但要求 MongoDB 4.4+ 服务端配合 - Node.js mongodb driver:v4+ 支持
directConnection=false+ 多地址,会自动探测并轮询,但默认不启用拓扑监控,需显式设置heartbeatFrequencyMS - Java MongoSyncDriver:依赖
com.mongodb.ConnectionString,多个 host 自动构建副本集感知逻辑,但对mongos列表仍倾向使用第一个可用节点,除非配置serverSelectionTimeoutMS和重试策略
更稳妥的做法是:交由 L4 LB 统一处理,应用只连一个 VIP 或域名,把连接管理复杂度下沉,避免各语言驱动行为不一致带来的隐患。
真正影响负载的关键其实是分片键和查询模式
即使 mongos 流量绝对均匀,如果所有查询都命中同一个分片(例如用 user_id 但值集中在 1~100),那么底层 shard 仍会过热,而 mongos 只是“背锅”。
- 分片键选择要兼顾基数高、写入分散、查询常用:避免用时间戳、自增 ID、低基数字段(如
status只有 3 个值) - 复合分片键可缓解热点,例如
{region: 1, created_at: 1}比单独created_at更利于分布 - 定期用
sh.status()观察 chunk 分布,用sh.splitAt()或sh.moveChunk()手动干预倾斜
连接层的均衡只是第一道防线,数据层的分布才是根因。别在 LB 上调参调到凌晨,结果发现 80% 查询都在 shard0001 上跑。


















