为什么单机撑不住

单机 MongoDB 的问题很直白:机器挂了数据就没了,宕机期间业务完全中断;机器性能到头了,读写都卡在硬件上限。要解决这两个问题,方向也直白:多副本保证可用性,分片扩展容量。MongoDB 把这两个能力内建为副本集与分片集群,副本集解决"挂了怎么办",分片解决"装不下、跑不动怎么办"。

理解这两个机制,是 MongoDB 运维与架构设计的分水岭。本文从副本集讲起,再讲分片集群的搭建与扩容,最后给出一份可直接落地的运维清单。

本文核心知识点
  • 副本集主从复制加自动故障转移。
  • 心跳节点间选举与健康探测。
  • 分片按分片键把数据分布到多组。
  • 均衡数据在各分片间自动迁移。
MongoDB 副本集与分片集群配图:蓝紫渐变背景中央蓝色多层圆柱数据库模型带顶部球体,蓝色连线连接左侧服务器机柜、粉色连线连接右侧服务器机柜,扁平科技插画
中央数据节点向四周副本节点复制数据,正是副本集高可用的意象。

副本集:主从与自动故障转移

副本集是一个由若干节点组成的高可用组:一个主节点负责读写,其余是从节点同步数据。主节点宕机后,剩余节点通过心跳投票选出新的主节点,通常几秒内完成,业务几乎无感知。标准部署是三节点:一主一从一仲裁,仲裁节点不存数据、只参与投票,成本最低。

// 初始化副本集
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "mongo-1:27017" },
    { _id: 1, host: "mongo-2:27017" },
    { _id: 2, host: "mongo-3:27017", arbiterOnly: true },
  ],
});

副本集还有一个常被忽略的用途:滚动维护。升级版本、调整配置都可以逐个摘节点操作,主节点切换后继续处理下一个,全程不中断服务。

读写分离与一致性

副本集默认读写都走主节点,从节点只做备份。读压力大时,可以把读分散到从节点:连接串里指定 readPreference=secondary。但要注意,从节点数据有复制延迟,刚写入就读从库可能读到旧数据。一致性要求高的读仍走主节点,能容忍延迟的报表、统计类查询走从节点。

// 连接串:读偏向从节点
mongodb://mongo-1,mongo-2,mongo-3/?replicaSet=rs0&readPreference=secondaryPreferred
读偏好怎么选
  • primary默认,强一致,全走主。
  • secondary只读从,接受延迟。
  • nearest就近读,跨机房友好。

写安全级别也要按业务定:w=1 主节点确认即可,吞吐高;w=majority 多数派确认,数据更稳,主节点挂掉也不丢已确认写入。对账、支付类关键写,用 majority。

分片集群与分片键

副本集解决可用性,容量与性能天花板要靠分片。分片集群由三部分组成:mongos 路由层接收请求,config server 保存元数据,若干个分片(每个分片本身是一个副本集)存数据。写入时按分片键路由到对应分片,数据天然分布到多组机器。

分片键的选择是成败关键:要有足够基数,避免大量数据落在同一个值上;要分布均匀,写入热点不能集中;查询要能带分片键,否则路由层全片扫描。常见好键:用户 ID 哈希、订单号、时间加随机后缀。

// 开启分片并建索引
sh.enableSharding("appdb")
sh.shardCollection("appdb.orders", { orderId: "hashed" })

分片键一旦定下来很难改,设计阶段就要想清楚业务形态。用哈希分片键可以强制打散热点,但范围查询会退化成全分片扫描,两者按查询模式权衡。

扩容与数据均衡

分片集群的扩容是平滑的:新加一个分片组,MongoDB 的均衡器会自动把数据 chunk 从高分片迁往新分片,迁移期间不影响读写。缩容则反着来,先把数据均衡走再下线分片。均衡器有默认节奏,避免与业务高峰冲突,迁移本身也消耗带宽与 IO,必要时限制迁移窗口。

// 查看集群分片状态
sh.status()

扩容前要做容量评估:当前数据量与增长速率决定新分片数量;单分片内存与磁盘规格决定能扛的 chunk 数。分片太碎(chunk 过多)会放大元数据开销,适当调大 chunk 大小可以减少迁移压力。