mgo.Dial连接分片集群必须使用mongos地址而非shard,否则绕过路由导致写入失败或数据不一致;正确格式为"mongodb://user:pass@mongos01:27017,mongos02:27017/?replicaSet=rs0",并需配合session.Copy()、SetMode、SetPoolLimit等规范用法。

mgo.Dial 连接分片集群时必须用 mongos 地址,不能直连 shard
直接 mgo.Dial("shard01:27018") 会绕过分片路由,导致写入失败或数据不一致。mgo 不识别分片拓扑,它只管连上一个 MongoDB 实例 —— 而分片集群的入口是 mongos 进程,不是 mongod。
常见错误现象:panic: not master、write to db failed: not master、查询返回空但插入无报错(实际写进了某个 shard 的主节点,却没走路由逻辑)。
- 正确地址格式是
"mongodb://user:pass@mongos01:27017,mongos02:27017/?replicaSet=rs0"(注意:多个 mongos 地址用逗号分隔,且必须加?replicaSet=参数才能启用自动故障转移) - 若集群启用了认证,
Source字段必须指定admin或对应权限数据库,不能填test -
mgo.DialWithInfo比mgo.Dial更可靠,尤其在带 auth 和 replicaSet 场景下;Dial仅支持简单 URL,无法设置超时、连接池上限等关键参数
session.Copy() 是并发安全的唯一方式,别复用全局 session
在分片集群高并发场景下,直接把一个 *mgo.Session 传给多个 goroutine,轻则写入乱序,重则 panic:connection closed 或 invalid cursor。因为 mgo.Session 内部套接字不是 goroutine-safe 的共享资源。
正确做法永远是:每次操作前调用 session.Copy(),操作完立刻 defer copiedSession.Close()。
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
session.SetMode(mgo.Monotonic, true)必须在Copy()后设置,而不是在原始 session 上设 —— 原始 session 的 mode 不影响副本 - 不要试图缓存
*mgo.Collection对象:它绑定了 session 实例,跨 goroutine 复用等于复用 session - 连接池大小由
session.SetPoolLimit(100)控制,这个值应略高于你的并发峰值,但不宜超过 mongos 的 maxIncomingConnections(默认 65536)
Update 和 Upsert 在分片键缺失时会失败,必须显式校验
对分片集合执行 c.Update(selector, update) 时,如果 selector 中不含分片键(shard key),mongos 无法路由到目标 shard,直接返回 error: "cannot update { _id: ... } on sharded collection without specifying the shard key"。
这不是 mgo 的 bug,而是 MongoDB 分片协议强制要求 —— 所有更新/删除必须能定位到唯一 shard。
- 插入时若未提供分片键字段,文档会被路由到“默认 chunk”,但后续更新就再也找不到它了
- 用
bson.M{"$set": bson.M{"field": "val"}}替代全量替换,避免意外覆盖分片键字段 - Upsert 尤其危险:如果 selector 不含分片键,mongos 会拒绝插入新文档(即使匹配不到),并报错而非静默失败
聚合查询需禁用 cursor batchSize,否则分片间结果合并可能超时
在分片集群上调用 c.Pipe(pipeline).All(&results) 时,mgo 默认使用游标批量拉取(batchSize=101),而 mongos 需要协调所有 shard 返回结果再合并。若某 shard 响应慢,整个聚合会卡住,最终触发 context deadline exceeded 或 i/o timeout。
这不是网络问题,是 mongos 的游标协调机制限制。
- 解决方案:在 pipeline 最后加
{"$limit": N},或手动控制 batchSize:pipe := c.Pipe(pipeline); pipe.Batch(500) - 避免在聚合中使用
$lookup跨库关联 —— 分片集群不支持跨分片 join,会直接报错unsharded collection - 时间范围查询务必带上分片键前缀,否则 mongos 只能广播到所有 shard,性能断崖式下降
Find、Update、Pipe,背后是否真被 mongos 正确解析并分发,得靠日志和 db.currentOp() 验证,不能只看 Go 层有没有 panic。

















