分片集群使用最佳实践

最近更新时间: 2026-03-13 09:03:00

分片集群为 MongoDB 的分布式版本,相较副本集,分片集群数据被均衡的分布在不同分片中, 不仅大幅提升了整个集群的数据容量上限,也将读写的压力分散到不同分片,以解决副本集性能瓶颈的难题,但分片集群的架构更加复杂,本文重点介绍使用TCloudFinanceZone MongoDB 分片集群时的注意事项。

分片集群组件

一个 MongoDB 分片集群由如下三个组件构成,缺一不可:

  • shard:每个分片是整体数据的一部分子集,每个分片都部署为副本集。
  • mongos:充当查询路由器,提供客户端应用程序和分片集群之间的接口。
  • config servers:配置服务器存储集群的元数据和配置,包括权限认证相关。

分片集群 sharding 方式及性能影响

MongoDB 分片集群提供三种 Sharding(数据分布)方式,分别为基于范围、基于 Hash、基于 zone/tag。不同的 Sharding 方式使用不同的业务,也会对性能产生不同的影响。

  • 基于范围
    优势:分片键范围查询性能较好,读性能较好。
    劣势:数据分布可能不均匀,存在热点。
  • 基于 Hash
    优势:数据分布均匀,写性能较好,适用于日志、物联网等高并发场景。
    劣势:范围查询效率较低。
  • 基于 zone/tag
    若数据具备一些天然的区分,如基于地域、时间等标签,数据可以基于标签来做区分。
    优势:数据分布较为合理。

分片键的选择

分片键是文档中的某一个字段,用来进行路由查询,分片键是不可变的,且必须有索引。
选择合适的片键对 sharding 效率影响很大,主要基于如下四个因素:

  • 取值基数
    取值基数建议尽可能大,如果用小基数的片键,因为备选值有限,那么块的总数量就有限,随着数据增多,块的大小会越来越大,导致水平扩展时移动块会非常困难。
    例如:选择年龄做一个基数,范围最多只有100个,随着数据量增多,同一个值分布过多时,导致 chunck 的增长超出 chuncksize 的范围,引起 jumbo chunk,从而无法迁移,导致数据分布不均匀,性能瓶颈。
  • 取值分布
    取值分布建议尽量均匀,分布不均匀的片键会造成某些块的数据量非常大,同样有上面数据分布不均匀,性能瓶颈的问题。
  • 查询带分片
    查询时建议带上分片,使用分片键进行条件查询时,mongos 可以直接定位到具体分片,否则 mongos 需要将查询分发到所有分片,再等待响应返回。
  • 避免单调底层或递减
    单调递增的 sharding key,数据文件挪动小,但写入会集中,导致最后一篇的数据量持续增大,不断发生迁移,递减同理。
    综上,在选择片键时要考虑以上4个条件,尽可能满足更多的条件,才能降低 MoveChuncks 对性能的影响,从而获得最优的性能体验。

分片集群 balance 介绍及相关参数

在一个分片集群内部,MongoDB 会把数据分为 chunks,后台进程 balancer 负责 chunk 的迁移,从而均衡各个 shard server 的负载,每个 chunk 包含一部分数据,chunk 的产生和迁移会导致 balance 的产生。

说明:

系统初始仅1个 chunk,chunk size 默认值64MB。

chunck 迁移时会造成集群的读写性能下降,因此需要通过适当配置 balance 活动窗口来避免 balance 对业务高峰期的影响,也可以通过命令来关闭 balance。

下面介绍管理 balance 的相关命令,若某些指令无权限执行,请提交工单处理。

  • 查看 mongo 集群是否开启了 balance

    mongos> sh.getBalancerState()
    true
    

也可通过执行 sh.status() 查看 balance 状态。

  • 查看是否正在有数据的迁移

    mongos> sh.isBalancerRunning()
    false
    
  • 设置 balance 窗口

  • 修改 balance 窗口的时间:

    db.settings.update(
       { _id: "balancer" },
       { $set: { activeWindow : { start : "<start-time>", stop : "<stop-time>" } } },
       { upsert: true }
    )
    
  • 删除 balance 窗口:

    use config
    db.settings.update({ _id : "balancer" }, { $unset : { activeWindow : true } })
    
  • 关闭 balance

  • 默认 balance 的运行可以在任何时间,迁移只需要迁移的 chunk,如需关闭 balance,可执行下列命令:

    sh.stopBalancer()
    sh.getBalancerState()
    
  • 停止 balace 后,查看是否有迁移进程正在执行,可执行下列命令:

    use config
    while( sh.isBalancerRunning() ) {
              print("waiting...");
              sleep(1000);
    }
    
  • 打开 balance

  • 如您需要准备重新打开 balance,可执行下列命令:

    sh.setBalancerState(true)
    
  • 当驱动版本不支持 sh.startBalancer() 时,可执行下列命令来重新打开 balance:

    use config
    db.settings.update( { _id: "balancer" }, { $set : { stopped: false } } , { upsert: true } )
    
  • 集合的 balance

  • 关闭某个集合的 balance:

    sh.disableBalancing("students.grades")
    
  • 打开某个集合的 balance:

    sh.enableBalancing("students.grades")
    
  • 查看某个集合是否开启了 balance:

    db.getSiblingDB("config").collections.findOne({_id : "students.grades"}).noBalance