高效分片和扩展 MongoDB 集群的最佳实践
选择更好的MongoDB分片键,监控平衡,并设计避免不必要分散-收集工作的查询。
高效分片与扩展MongoDB集群的最佳实践
MongoDB分片将集合分布到多个分片上,这样单个副本集就不必承载所有数据或流量。它可以解决实际的扩展问题,但一个糟糕的分片键可能会造成热点分片、缓慢的分散-收集查询以及难以撤销的操作工作。
当你已经处理了基础知识:索引、模式设计、硬件规模和查询调优后,当单个副本集无法再处理你的数据大小、写入吞吐量或读取工作负载时,才使用分片。
理解分片集群的核心组件
一个功能性的分片集群依赖于多个相互连接的组件协同工作:
- 分片(分片副本集): 每个分片通常是一个副本集,持有总数据集的一个子集。数据在这些分片之间进行分区。
- 查询路由器(Mongos进程): 这些进程接收客户端请求,确定哪个分片持有所需数据(基于元数据),路由查询,聚合结果,并将其返回给客户端。它们是无状态的,并且高度可扩展。
- 配置服务器(Config服务器): 这些专用的副本集存储元数据(集群映射),告诉
mongos进程特定数据块位于何处。它们对集群操作至关重要,必须保持高可用性。
关键策略1:选择最佳分片键
分片键是分片中最关键的决策。它决定了数据如何在分片之间进行分区。一个精心选择的分片键能带来均匀的数据分布和高效的查询路由;一个糟糕的键会导致热点和集群不平衡。
有效分片键的特征
一个理想的分片键应具备三个主要特征:
- 高基数: 键应具有许多唯一值,以允许细粒度的分区。低基数会导致整体块数减少。
- 高写入频率/均匀分布: 写入应均匀分布在所有分片键值上,以防止单个分片过载(热点)。
- 查询模式: 查询应理想地针对分片键,以实现定向查询(路由到特定分片)。需要扫描所有分片的查询(分散-收集查询)会慢得多。
分片方法及其影响
MongoDB支持两种主要的分片方法:
- 哈希分片: 对分片键值使用哈希函数。这确保了优秀的数据分布,即使对于顺序键也是如此,通过将写入分散到所有可用分片。最适合高写入吞吐量且查询局部性不太重要的场景。
- 范围分片: 基于分片键的范围对数据进行分区(例如,ID为1-1000的所有用户进入分片A)。最适合查询模式与范围查找对齐的场景(例如,按日期范围或字母ID范围查询)。
⚠️ 关于范围分片的警告: 如果你的数据插入模式遵循严格递增的序列(如时间戳或自增ID),范围分片将导致所有写入都落在最新的块上,从而在最后一个分片上产生显著的热点。
示例:应用哈希分片
如果你选择像userId这样的字段,并且你的查询经常按它过滤,哈希它可以均匀分布写入:
// 选择数据库和集合
use myAppDB
// 对userId字段进行哈希分片
sh.shardCollection("myAppDB.users", { "userId": "hashed" })
关键策略2:管理数据分布和平衡
即使有完美的分片键,由于查询模式演变或初始负载不平衡,数据块(存储在分片上的数据物理单元)也可能变得大小不均或分布不均。平衡器进程处理这些块的迁移。
监控平衡器
监控集群的平衡指标至关重要。不平衡的块会导致某些分片上的资源利用不足,而其他分片则过载。
在shell中使用sh.status()命令查看整体状态,包括哪些块正在迁移。
控制平衡器
虽然平衡器自动运行,但你可以临时禁用它,例如在高维护窗口或大批量导入期间,以控制资源消耗:
// 检查当前状态
sh.getBalancerState()
// 临时禁用平衡
sh.stopBalancer()
// ... 执行维护或大批量导入 ...
// 完成后重新启动平衡
sh.startBalancer()
最佳实践: 永远不要永久禁用平衡器。如果你禁用了它,请安排定期检查,以确保随着应用程序的增长数据保持均匀分布。
块大小考虑
块不应太小,因为这会创建过多的元数据开销并减慢平衡器。相反,块太大会导致迁移缓慢和负载平衡机会不佳。
- 默认块大小: MongoDB的默认块大小通常适用于许多集群。在更改之前,请查阅你的MongoDB版本的文档。
- 调整块大小: 仅当你有一个明确的操作原因时才更改块大小,例如迁移时间过长或元数据开销过大。支持的方法在不同MongoDB版本中有所变化,因此在应用之前请验证当前命令。
关键策略3:优化读写性能
分片改变了读写路由的方式,因此需要特定的性能调优。
定向查询与分散-收集查询
- 定向查询: 包含分片键(或范围分片中的分片键前缀)的查询允许
mongos路由器将请求直接发送到一个或几个分片。这些查询很快。 - 分散-收集查询: 不使用分片键的查询必须发送到每个分片,增加了网络延迟和处理开销。
可操作提示: 设计应用程序查询以尽可能利用分片键。对于必须广泛扫描的查询,考虑使用倾向于副本集次要成员的读取偏好,以将负载与主要成员隔离。
分片集群中的读取偏好
分片集群在客户端级别处理读取偏好。确保你的应用程序代码根据操作的关键性正确设置读取偏好:
primary(默认): 读取到每个分片副本集的主要成员。nearest: 读取到地理或网络距离应用程序最近的副本集成员。secondaryPreferred: 除非没有次要成员可用,否则读取发送到次要成员,这对于将报告或分析查询从主要成员卸载很有用。
避免索引陷阱
确保在查询过滤器或排序操作中频繁使用的字段上存在索引,特别是分片键和分片键的任何前缀字段。分片之间的索引不一致也可能导致意外的分散-收集查询,如果一个分片无法使用索引。
稳定性的操作最佳实践
维护一个稳定、高性能的分片集群需要持续的操作警惕。
1. 分片键更改
选择分片键时,要像更改它成本很高一样,因为通常确实如此。较新的MongoDB版本支持比旧版本更多的分片键细化和一些分片键值更新,但规则取决于你的版本、键模式和事务要求。不要指望在生产流量开始后轻松重写。
2. 配置服务器弹性
配置服务器是集群的大脑。如果它们不可用,客户端无法确定数据所在位置,从而有效停止操作。
- 始终将配置服务器部署为副本集(至少三个成员)。
- 确保配置服务器具有快速存储,并且不受应用程序工作负载的负担。
3. 容量规划
通过监控单个分片成员的CPU、内存、磁盘I/O、存储增长、复制延迟和块分布来规划增长。在某个分片成为瓶颈之前增加容量,而不是依赖一个固定的利用率百分比。
要点
MongoDB中的分片是一个扩展工具,而不是绕过数据建模的捷径。选择一个能分散写入并匹配你最重要查询的分片键,启动后监控平衡,并尽可能保持应用程序查询定向。