文档数据库 MongoDB 版支持分片集群实例架构,能够提供可横向扩展的 MongoDB 服务。分片集群通过将大型集合自动分割到不同节点,来满足大规模高性能场景下的容量和性能需求。本文介绍使用 MongoDB 分片集群的相关建议供您参考。
分片集群使用场景
在如下场景中建议使用 MongoDB 分片集群:
- 受单机 CPU、内存、网卡等资源限制,读写能力无法扩展。
分片集群注意事项
- 所有需要分片的集合都必须保证分片键上存在索引,该索引可以是分片键本身的索引,也可以是复合索引。因此在设置分片键时,需注意如下几点:
- 若使用了复合索引,分片键必须是索引的前缀,否则在分片键时可能会出现 Uniqueness can't be maintained unless shard key is a prefix 相关的报错。
- 当需要对已分片数据进行修改时,如果 update 请求的 filter 中未携带分片键字段并且设置了选项 upsert:true 或者multi:false,那么 update 请求会返回 An upsert on a sharded collection must contain the shard key and have the simple collation 错误。为避免影响线上业务,建议在业务低峰期执行修改。
- 为已有集合(即集合中已存在数据)开启分片后,分片会马上进行搬迁,因此从开启分片到数据完全均衡的这段时间内,primary shard 的负载可能会增加。为避免影响线上业务,建议在业务低峰期执行分片操作。
- 针对 MongoDB 4.0 和 4.2 版本实例,当为已有集合(即集合中已有数据)开启分片时会出现短暂的锁表禁写,在此期间写入的不带 Shard key 的 collection 将无法被 Mongos 查询,但您可以连接至具体的 Shard 进行查询。
- 强烈建议密切关注 MongoDB 实例的表数据量,并提前为实例开启分片。
分片集群使用建议
设置合适的 Shard、Mongos 数量
分片(Shard)和分片代理(Mongos)是 MongoDB 分片集群实例中的重要组成部分。您可以根据业务场景需要,参考以下方法确定 Shard 和 Mongos 数量:
- 分片集群仅用于解决海量数据的存储问题,且访问量不多。
- 例如一个 Shard 的最大存储量为 A, 需要的存储总量是 B,那么您的业务需要的 Shard 和 Mongos 数量可以参考如下公式进行计算:
- Shard 数量 = B ➗ A ➗ 0.75 (假设容量水位线为 75%)
- Mongos 数量:至少部署 2 个 Mongos 做高可用
- 分片集群用于解决高并发写入(或读取)数据的问题,但总的数据量很小,即 Shard 和 Mongos 需要满足读写性能需求。
- 例如一个 Shard 的最大 QPS 为 C,一个 Mongos 的最大 QPS 为 D,业务需要的总 QPS 为 Q,那么您的业务需要的 Shard 和 Mongos 数量可以参考如下公式进行计算:
- Shard 数量 = Q ➗ C ➗ 0.75(假设负载水位线为 75%)
- Mongos 数量 = Q ➗ D ➗ 0.75
说明
- 如果分片集群同时解决上述两个问题,则按照需求更高的指标进行预估。
- 上述计算方法是基于分片集群中数据和请求都均匀分布的理想情况下进行预估,实际情况下,分布可能并不均匀,为了让系统的负载尽量均匀,您需要选择合理的 shard key。更多详情,请参见设置合适的 shard key。
- Shard 和 Mongos 支持的服务能力,需要您根据业务访问特性实测得出。
分片集群中数据的分片以集合为基础单位,集合中的数据通过分片键被分成多个部分。分片键是在集合中选择的一个或多个合适的字段,数据拆分时以该分片键的值为依据均衡地分布到所有分片中。如果您没有选择到合适的的分片键,可能会降低集群的使用性能,出现执行分片语句时执行过程卡住的问题。
说明
- 从 MongoDB 5.0 起,您可以通过 reshardCollection 命令来修改分片键,实现数据的重新分配。修改分片键的具体操作步骤,请参见 Reshard collection。
- Key 分布足够离散(sufficient cardinality)
- 写请求均匀分布(evenly distributed write)
- 尽量避免 scatter-gather 查询(targeted read)
说明
如果所选分片键不具备以上所有特点,将会影响集群的读写扩展性。例如,某字段经常被更新或经常被查询,若将其作为分片键可能会导致数据分片不均衡从而出现读写热分片,严重限制分片的优势。因此,您需要根据业务的数据分片情况、常用查询及写入的数据等场景,调整您的分片键。
- 您可以参考如下信息判断设置的分片键是否能够满足业务需求。
Chunk size 与 jumbo chunk
- MongoDB 5.0 及之前版本的 chunk size 默认值为 64 MB,您可以根据业务数据量和分片使用场景修改 chunk size 值,支持的取值范围为 1MB ~ 1024 MB。修改 chunk size 值时需要注意如下几点:
- chunk 的自动拆分操作仅发生在插入或更新数据时。
- 较小的 chunk size 值会使数据分布更均匀,但会带来更频繁的迁移,导致查询路由开销增加。如果调小了 chunk size 值,MongoDB 会耗费一些时间将数据从原有 chunk 拆分到新 chunk,且此操作不可逆。
- 较大的 chunk size 值通常迁移较少,查询路由和网络负载也较低,但很可能会导致数据分布不均匀,限制分片优势。如果调大了 chunk size 值,已存在的 chunk 只会等到新的插入或更新操作将其扩充至新的大小。
- 当数据持续写入 chunk 并超过 chunk size 值时,且由于某些原因无法拆分(例如一个 chunk 即为单个分片键值)时,就会被标记为 jumbo chunk。Jumbo chunk 无法被 balancer 迁移,进而可能导致负载不均衡,影响数据库性能。
- Chunk 被标记为 jumbo chunk 后,您可以参考如下方法进行处理:
- 若该 chunk 可以被拆分,您可以尝试通过 sh.splitAt() 或 sh.splitFind() 命令将其拆分。拆分成功后,MongoDB 会自动清除该 chunk 的 jumbo 标记。关于命令的更多详情,请参见 sh.splitAt() 和 sh.splitFind() 。
- 若该 chunk 不可再拆分,您可以尝试通过 clearJumboFlag 命令手动清除该 chunk 的 jumbo 标记。关于命令的更多详情,请参见 clearJumboFlag。
说明
- 您可以尝试调大 chunk size,当 chunk 未超过 chunk size 时,jumbo 标记最后也会被清理。但随着数据的写入,jumbo chunk 仍然会出现,根本的解决办法还是需要设置合适的 shard key。更多详情,请参见设置合适的 shard key。
负载均衡
MongoDB 均衡器(Balancer)会周期性的检查分片间的 chunk 数量是否存在不均衡,当各个 Shard 分片之间的 chunk 数量差异达到指定的迁移阈值时,均衡器会在分片间自动迁移数据,实现数据在分片上的均匀分布。
MongoDB 均衡器默认启用,您也可以通过 sh.stopBalancer() 命令手动关闭均衡器。更多详情,请参见 Disable balance。 均衡器支持在线数据迁移,但数据迁移过程会对集群负载有较大影响,建议将均衡器的迁移时间设置在业务低峰期。您可以通过如下命令中的 activeWindow 参数来指定均衡器的活动时间窗口。
{ $set: { activeWindow : { start : "<start-time>", stop : "<stop-time>" } } },
说明
- 请根据您的业务场景将上述代码中的 <start-time> 和 <stop-time>换成指定的时间,例如 { start : "01:30", stop : "04:30" }。更多详情,请参见 Modify balancer's window。
- 上述时间以 MongoDB 分片集群实例所在地域的时区为准,其中:
数据分片设置实操
使用场景
某汽车自动驾驶应用需要通过 MongoDB 分片集群存储海量的车辆自动驾驶日志。假设车辆数量在百万级别,车辆每 10 秒就会向 MongoDB 上传一次日志数据,日志包含车辆 ID(VehicleId)和时间戳(Timestamp)信息,业务需要查询某辆车在某个时间内的日志信息。
Shard key 设置方案
根据上述使用场景和查询要求,可选的 shard key 设置方案见下表。
Shard key 设置步骤
本文根据上述使用场景和查询请求要求,以推荐的方案一(即组合车辆 ID 和时间戳作为 shard key,进行范围分片)为例,详细介绍 MongoDB 分片集群 shard key 的设置步骤。
说明
下述示例中以数据库 IoV,集合 AutonomousCar,字段 VehicleId 和 Timestamp 为例介绍相关操作步骤。
- 执行 use <数据库名称> 命令进入目标数据库。
db.<collection>.getShardDistribution()
db.AutonomousCar.getShardDistribution()
- 您可以选择如下任意一种方法启用目标数据库的分片功能。
通过 Mongo shell 工具登录目标分片集群实例后,直接执行如下命令:
sh.enableSharding("<database>")
命令示例如下。
sh.enableSharding("IoV")
通过 Mongo shell 工具登录目标分片集群实例后,先执行 use admin 命令进入 admin 数据库,然后执行如下命令:
db.runCommand({enablesharding:"<database>"})
命令示例如下。
db.runCommand({enablesharding:"IoV"})
- 确认 MongoDB 均衡器(Balancer)是否已开启,命令如下。
- 若返回结果为 true 表示均衡器已启用,当各个 Shard 分片之间的 chunk 数量差异达到指定的迁移阈值时,均衡器会在分片间自动迁移数据,实现数据在分片上的均匀分布。更多详情,请参见 Check balancer state。
- 若返回结果为 false 表示均衡器未启用,您可以通过 sh.startBalancer() 命令开启均衡器。更多详情,请参见 Enable balancer。
说明
从 MongoDB 4.2 起,通过 sh.startBalancer() 命令开启均衡器时,会默认打开分片集群的自动分裂功能,更多详情,请参见 sh.enableAutoSplit。 通过 Mongo shell 工具登录目标分片集群实例后,直接执行如下命令。
sh.shardCollection("<database>.<collection>",{"<keyname>":<value> })
命令示例如下。
sh.shardCollection("IoV.AutonomousCar",{"VehicleId":1,"Timestamp":1})
通过 Mongo shell 工具登录目标分片集群实例后,先执行 use admin 命令进入 admin 数据库,然后执行如下命令。
db.runCommand({shardcollection:"<database>.<collection>",key:{"keyname":<value> }})
命令示例如下。
db.runCommand({shardcollection:"IoV.AutonomousCar",key:{"VehicleId":1,"Timestamp":1}})
- 在目标数据库中,执行如下命令查看数据库在各分片节点的数据存储情况。