不能直接连主mongos做大屏查询,因其聚合、$lookup、$group等操作会耗尽连接和CPU,广播请求拖慢写入,导致writeConcern超时和写入卡顿。

为什么不能直接连主 mongos 做大屏查询
大屏轮询查聚合、$lookup、$group 会吃光 mongos 连接和 CPU,尤其在分片键不参与过滤时,请求被广播到所有 shard,拖慢写入链路。真实线上见过 mongos 因大屏查询堆积导致 writeConcern 超时、应用写入卡顿的案例。
- 主
mongos默认承担全部读写流量,无读写分离机制 - 大屏常带
$facet或多层$unwind,单次查询耗时波动大,干扰写入事务响应时间 - 即使加了
readPreference=secondaryPreferred,分片集群里 secondary 不一定有最新数据(存在复制延迟),且 mongos 仍需协调多个 shard 的结果
单独部署只读 mongos 的实操要点
这不是“换个连接地址”那么简单,关键在配置隔离和路由控制。只读 mongos 必须指向同一套 config server,但要禁用写能力并限制其访问权限。
- 启动时加参数:
--configdb <config_server_list>(必须和主 mongos 一致),但**不**加--bind_ip绑定生产写入网段,只监听大屏服务所在子网 - 在
mongos配置文件中显式关闭写支持:setParameter: { enableLocalhostAuthBypass: false }+ 配合角色最小化(见下条) - 给大屏应用分配专用账号,仅授予
clusterMonitor和目标数据库的read角色,**禁止**readWrite或任何 clusterAdmin 权限 - 验证是否生效:用该账号执行
db.runCommand({insert:"test", documents:[{}]})应返回"not authorized"
大屏查询必须绕开的三个分片陷阱
即便用了只读 mongos,没写对查询逻辑,照样会打垮整个集群。核心原则是:让 mongos 能把请求精准路由到 1 个 shard,而不是 fan-out 到全部。
- 避免全集合扫描:任何不含分片键(或分片键前缀)的
find()或aggregate()都会广播——例如按user_id分片,却查{status: "active"} -
$lookup跨库时默认失效:只读 mongos 不支持跨分片的$lookup(除非被 lookup 的集合也在同分片上)。改用应用层 join 或预聚合宽表 - 时间范围查询别用
new Date():JS 执行时间在 mongos 上不一致,建议传入服务端生成的 ISO 字符串,如{"ts": {"$gt": "2024-06-01T00:00:00Z"}}
如何确认只读 mongos 真的没影响主集群
最直接的方式不是看日志,而是抓 mongos 自身指标和 config server 的操作日志。重点盯两个信号:
- 登录只读
mongos,执行db.currentOp({secs_running: {$gt: 5}}),检查是否有长时间运行的写操作(正常应为空) - 在 config server 上查
config.mongos集合,确认只读实例的ping时间稳定,且up字段为true;再查config.locks,确保没有balancer相关锁被它持有 - 网络层面:用
tcpdump -i any port 27017 and host <只读_mongos_ip>抓包,确认只有来自大屏服务 IP 的连接,且无向 primary shard 的写命令(如 opCode=2004)
真正难的是分片键设计阶段就预留好大屏路径——等上了生产再改,往往只能靠冗余宽表硬扛。这点容易被忽略,但比配 mongos 花费更多精力。










