ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

MongoDB Balancer 源码深度解析:均衡、去碎片化与自动合并的策略与实现

MongoDB Balancer 源码深度解析:均衡、去碎片化与自动合并的策略与实现 MongoDB Balancer 源码深度解析均衡、去碎片化与自动合并的策略与实现【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo分片集群Sharded Cluster中数据分布的均衡程度直接决定了查询路由效率与各分片Shard的负载水平。MongoDB 的Balancer均衡器正是运行在配置服务器Config Server主节点上的后台守护进程它持续监控分片集合sharded collection的数据分布并下发命令改善这种分布。本文以当前仓库中 src/mongo/db/s/balancer/README.md 为骨架结合 balancer.h、balancer.cpp 等源码实现系统讲解 Balancer 的双线程架构、三大动作策略块选择、去碎片化、自动合并的工作流程、核心判定阈值以及全部可调参数帮助读者既掌握运维层面的开关与调优也理解底层调度原理。一、Balancer 是什么职责与基本能力Balancer 是构建在分片sharding之上的一类应用它监控分片集合的数据分布情况并下发命令来改善该分布。它的基本特性包括默认启用无需任何手工配置即可运行可随时关闭/重新开启既可以对整个集群cluster关闭或开启也可以对单个集合per-collection进行控制支持均衡窗口balancing window可以配置为每天仅在特定时间段内运行。从源码看Balancer 是一个继承自ReplicaSetAwareServiceConfigSvr的ReplicaSetAwareService见 balancer.h这意味着它跟随配置服务器副本集的主节点PRIMARY生命周期启动主节点当选step up时通过initiate()拉起均衡线程主节点退位step down时通过requestTermination()与joinTermination()有序终止线程。Balancer 作为配置服务器主节点的后台守护进程由两个相互独立的线程组成它们下发由动作策略action policies生成的请求。每个线程在执行期间都会查询不同的均衡策略以决定下一步执行哪些操作。三个动作策略分别是策略职责ChunkSelectionPolicy常规集合均衡动作拆分块、迁移块DefragmentationPolicy集合去碎片化合并小块AutoMergerPolicy合并连续块在 balancer.h 中可以看到这三个策略实例均作为Balancer的成员存在_chunkSelectionPolicy、_defragmentationPolicy、_autoMergerPolicy此外还包含_clusterStats集群统计、_commandScheduler命令调度器与_moveUnshardedPolicy未分片集合迁移策略。二、主线程MainThread轮次驱动的拆分与迁移主线程持续运行但以轮次rounds为单位推进每两轮之间存在一段延迟当前仓库中轮次默认间隔为kBalanceRoundDefaultInterval(10 * 1000)即 10 秒。主线程负责下发两类操作拆分splits由块选择策略ChunkSelectionPolicy生成迁移migrations分别来自块选择策略与去碎片化策略DefragmentationPolicy。一个完整的操作周期如下面的时序图所示在每个均衡轮次中主线程先派发一批拆分命令并等待它们全部完成然后派发一批迁移命令并等待全部完成轮次内下发的所有命令都必须在轮次结束前被等待完毕遇到任何错误只记录日志主线程本身忽略错误继续运行。从 balancer.h 可以看到主循环对应私有方法_mainThread()轮次生命周期由_beginRound()/_endRound()管理轮次间的等待通过_sleepFor()条件变量等待 超时实现而跨区域边界的块拆分由_splitChunksIfNeeded()完成批量迁移则由_doMigrations()调度。Jumbo chunks超大块的处理如果主线程为一个大于两倍最大块大小2 × max chunk size的块下发迁移命令迁移会失败并告知 Balancer 该块过大、无法迁移。此时主线程会尝试拆分该块拆分成功问题解决正常继续拆分失败例如块内相同分片键值的文档过多找不到合适的拆分点该块会被标记为jumbo超大块。这个 jumbo 标记会被块选择策略用来避免在未来继续选中这些大块进行迁移从而防止无效迁移反复重试。需要注意的是jumbo 块并非永远无法移动当用户下发moveChunk/moveRange命令并携带forceJumbo: true参数时jumbo 块仍可以被迁移出所在分片。用户可以利用该选项手动将 jumbo 块重新分布到集群中的其他分片。在源码中迁移请求的载体结构 MigrateInfo 便包含ForceJumbo forceJumbo字段用于在迁移时显式指定是否强制迁移 jumbo 块。三、辅助线程Secondary Thread异步命令执行辅助线程等待在条件变量上必须由其他进程通常是客户端线程或主均衡线程发出信号后才开始工作。它负责下发来自以下两个策略的非迁移类命令去碎片化策略merge合并、datasize数据量统计等命令自动合并策略AutoMergerPolicymergeAllChunksOnShard命令。当两个策略同时处于激活状态时辅助线程会随机从去碎片化策略或自动合并策略中挑选命令下发。与主线程同步等待每批命令完成不同辅助线程是异步派发服务器命令的任意时刻最多允许有50 个未完成outstanding的操作。该上限对应 balancer.h 中的常量kMaxOutstandingStreamingOperations 50并由成员_outstandingStreamingOpsAtomicint实时计数。为了降低下发命令对从节点secondary nodes上目录缓存刷新catalog cache refresh速率的影响辅助线程还引入了节流参数在两个调度操作之间插入等待。默认值与含义如下定义见 sharding_config_server_parameters.idl参数默认值含义chunkDefragmentationThrottlingMS10001 秒两次不同的去碎片化动作下发之间的最小间隔即通过configureCollectionBalancing开启集合去碎片化后Balancer 下发的相邻两次mergeChunks/splitChunk命令的最小时间间隔autoMergerThrottlingMS1500015 秒针对同一集合的两次自动合并命令下发之间的最小间隔对应源码位置chunkDefragmentationThrottlingMS 与 autoMergerThrottlingMS两者均为Atomicint32_t可在启动时startup或运行时runtime动态修改。四、数据基础ClusterStatistics 与 CollectionDataSizeInfoForBalancing两个均衡策略做出的所有迁移决策都依赖于集群数据分布信息。每个均衡策略都持有一个 ClusterStatistics 的引用——这是一个接口允许策略获取集群的数据分布与分片利用率统计包括每个块chunk归属哪个分片集合定义的区域zones每个块属于哪个区域。除此之外块选择策略还需要更细粒度的信息——每个集合在每个分片上的数据量大小它通过 CollectionDataSizeInfoForBalancing内部持有ShardDataSizeMap类型的分片→数据量映射来跟踪这一信息。正是基于这些数据量统计策略才能判断哪个分片过载、哪个分片欠载从而做出迁移决策。五、ChunkSelectionPolicy块选择策略块选择策略 负责生成操作以维持集群内分片集合的平衡。判断集合是否平衡的标准是所有分片拥有的数据量是否大致相等。拆分Splits当某个块跨越区域边界zone boundaries时块选择策略会生成拆分请求。拆分命令会创建出一个更小的块其min 与 max 值恰好等于区域边界值从而使区域的边界能够精确对应到块边界这是区域约束生效的前提。迁移Migrations块选择策略会扫描所有分片集合生成一份迁移清单让 Balancer 据此把数据更均匀地分布到各分片。对于每个集合策略会选出所有可迁移的范围range并按以下优先级排序排水分片优先如果某个范围位于正在被移除draining / 排水的分片上优先选择该范围区域违规优先如果某些范围违反了区域约束落在了错误的分片上选择这些范围均衡迁移兜底若以上两种情况都不存在策略为了达到每个分片数据量相等的目标会找出负载最高的分片数据量最多与负载最低的分片数据量最少并选择从数据量较大的分片移出范围——前提是两者数据量之差大于 3 倍最大块大小3 × max chunk size。在以上三种场景中范围都会被移动到仍然可用且负载最低的分片。所谓可用是指该分片未处于排水状态并且没有参与当前均衡轮次中已被选中的任何其他迁移。这一优先级逻辑与 balancer_policy.h 中定义的迁移原因枚举高度对应enum MigrationReason { none, drain, zoneViolation, chunksImbalance }——即排水区域违规块数/数据量不均衡三种迁移动机外加无迁移。块选择策略会在每个均衡轮次中尽可能多地提交迁移。但需要注意去碎片化策略在迁移下发上拥有更高优先级——任何正在为去碎片化而迁移数据的分片块选择策略都无法再向它调度迁移。六、DefragmentationPolicy去碎片化策略集合去碎片化Collection defragmentation的目标是在保证数据可路由的前提下尽可能多地合并集合中的块以减少集合的块总数。块是存储在分片路由表sharding routing table中、用于路由命令的用户数据细分单位因此减少集合的块数量可以缩小路由信息routing information的规模加快分片刷新sharding refresh的速度。去碎片化策略 负责在整个去碎片化过程中生成各类命令。去碎片化由三个阶段构成MergeAndMeasureChunks合并并测量块MoveAndMergeChunks迁移并合并块MergeChunks合并块关键约束一旦用户对某个集合发起去碎片化在去碎片化运行期间该集合只会被去碎片化策略考虑不会参与常规均衡configureCollectionBalancing命令会持久化这一状态。在 balancer.h 中提供了abortCollectionDefragmentation()方法用于用户主动中止某个集合的去碎片化进程。第一阶段MergeAndMeasureChunksPhase合并并测量第一阶段由集合内所有块的 merge 与 datasize 命令组成在每个分片上去碎片化策略会为每组连续块生成一个 merge 请求每次 merge 完成后会为刚创建出的新块生成一个datasize命令datasize 的结果会被持久化到config.chunks条目中供第二阶段使用去碎片化结束后这些值会被清理merge 与 datasize 命令由辅助线程以并行方式下发并为每个请求调度一个回调将执行结果通知给去碎片化策略。第二阶段MoveAndMergeChunksPhase迁移并合并第二阶段是整个流程中最复杂的阶段。它使用第一阶段计算出的数据量为任何小于集合最大块大小 25% 的块即小块生成迁移请求每个小块会被迁移到一个包含与其连续块的分片如果存在两个这样的候选分片则按以下标准按重要性降序排列为候选分片打分该候选分片是否就是此块当前所在的分片该块是否比候选分片上将要与之合并的块更小将该块与候选分片上的块合并后得到的块是否足够大、从而不再是小块该候选分片的数据量是否比另一个候选分片更少每回答一个是候选分片获得更高分数第一个问题的权重最高最后一个问题权重最低。得分最高的候选分片将成为该块迁移的目的分片。在主线程完成迁移之后去碎片化策略会为迁移过来的块和它去相遇的那个连续块生成一个 merge 请求该 merge 动作由辅助线程执行。此过程循环进行直到所有块都大于最大块大小的 25%。在源码中这一 25% 阈值由 balancer_defragmentation_policy.h 中的常量kSmallChunkSizeThresholdPctg 25定义各阶段通过DefragmentationPhase抽象接口getNextPhase()、popNextMigration()、isComplete()等统一驱动保证每个集合的去碎片化状态机可以独立推进。第三阶段MergeChunksPhase合并块最后一个阶段与第一阶段非常相似但不再下发 datasize 命令。该阶段会为所有分片上的全部连续块生成 merge 请求。与第一阶段相同所有这些命令都由辅助线程下发。错误处理Error Handling去碎片化过程中存在两类错误错误类型处理方式可重试错误retriable策略会重复下发同一操作直到成功为止不可重试错误non-retriable去碎片化会重新开始执行退回到某个阶段的起点具体回退规则对于MergeAndMeasureChunksPhase与MergeChunksPhase从该阶段起点重新开始对于MoveAndMergeChunksPhase则回退到MergeAndMeasureChunksPhase重新开始。七、AutoMergerPolicy自动合并策略从v7.0开始分片组件中新增了auto-merger自动合并器它会周期性地扫描块识别出可合并mergeable的块并将它们合并squash到一起。除非被显式禁用自动合并器会按照 autoMergerIntervalSecs 指定的周期可配置参数默认 1 小时 3600 秒检查是否存在可合并的块并最终通过辅助线程下发mergeAllChunksOnShard动作。自动合并策略实现的算法可以概括如下伪代码与 auto_merger_policy.h 的注释描述一致While(true): -- Identify all the shard, namespace pairs for which there are mergeable chunks -- While(there are mergeable chunks): ---- For each shard: ------ For each namespace: -------- Schedule a mergeAllChunksOnShard action (max 10 actions per time) ------ Apply throttling of autoMergerThrottlingMS -- Sleep for autoMergerIntervalSecs其中每次最多 10 个动作的上限对应 auto_merger_policy.h 中的常量MAX_NUMBER_OF_CONCURRENT_MERGE_ACTIONS 10配合 autoMergerThrottlingMS默认 15 秒实现同一集合上两次合并命令的间隔控制。可合并块mergeable chunks的定义属于同一集合的两个或更多连续块在满足以下条件时视为可合并它们归属于同一个分片它们的历史history可以被安全清理而不会破坏事务transactions或快照读snapshot reads。另外jumbo 块不可合并因为 jumbo 块无法参与迁移合并前需要先移动块而 jumbo 块被排除在常规迁移之外。形式上两个或更多连续的非 jumbo 块必须满足以下条件才能被合并从未被迁移过或者涉及其中任一块的最后一次迁移发生在距今超过minSnapshotHistoryWindowInSeconds快照历史窗口默认 300 秒并且距今超过transactionLifetimeLimitSeconds事务生命周期上限默认 60 秒。该条件的物理意义是只有确定旧历史已经超出事务与快照读所需保留的窗口合并才安全——因为合并块会改写块历史若仍有活动事务或快照读依赖旧历史则可能导致读取失败。合并示例以下示例假设所有块的历史均为空且没有任何块被标记为 jumbo因此所有属于同一分片的连续区间都是可合并的。考虑路由表中属于集合db.coll分片键为x的如下块区间CHUNKMINMAXSHARDAx: 0x: 10Shard0Bx: 10x: 20Shard0Cx: 20x: 30Shard0Dx: 30x: 40Shard0Ex: 40x: 50Shard1Fx: 50x: 60Shard1Gx: 60x: 70Shard0Hx: 70x: 80Shard0Ix: 80x: 90Shard1当自动合并器运行时可合并块被合并最终结果如下CHUNKMINMAXSHARDA-B-C-Dx: 0x: 40Shard0E-Fx: 40x: 60Shard1G-Hx: 60x: 80Shard0Ix: 80x: 90Shard1可以看到同一分片上的连续区间被各自合并为一个块A-B-C-D、E-F、G-H而I在 Shard1 上虽与 G-H 相邻但由于分片归属不同I 在 Shard1G-H 在 Shard0不满足同一分片条件因此保持独立。八、相关可调参数速查结合 sharding_config_server_parameters.idl 与 README 描述Balancer 及关联组件常用的可调参数汇总如下参数默认值说明balancerMigrationsThrottlingMs1000 ms相邻两个均衡轮次之间的最小间隔设置过低可能导致 CRUD 因无法建立稳定分片版本而失败balancerChunksSelectionTimeoutMs5000 ms每个均衡轮次中 Balancer 用于决策迁移哪些范围的最大耗时chunkDefragmentationThrottlingMS1000 ms去碎片化期间相邻两次 merge/split 命令的最小间隔autoMergerIntervalSecs3600 s1 小时自动合并器两次扫描之间的间隔autoMergerThrottlingMS15000 ms15 秒同一集合相邻两次自动合并命令的最小间隔autoMergerMaxTimeProcessingChunksMS500 ms自动合并器调度的mergeAllChunksOnShard命令在单次请求内查找可合并块的最大耗时autoMergerMaxChunksToMerge100最小值 2单个mergeAllChunksOnShard请求中最多合并的块数以上参数均支持在启动时startup或运行时runtime通过setParameter动态调整autoMergerIntervalSecs为Atomicint其余大多为Atomicint32_t/Atomicbool为生产环境按需调优提供了灵活手段。九、源码导览从文档到实现如果希望进一步深入源码推荐按以下路径阅读线程与轮次balancer.cpp 中的_mainThread()主线程轮次循环与_consumeActionStreamLoop()辅助线程消费动作流对应 balancer.h策略接口与迁移模型balancer_policy.hMigrateInfo、SplitInfo、MigrationReason、CollectionDataSizeInfoForBalancing三个策略实现balancer_chunk_selection_policy.cpp、balancer_defragmentation_policy.cpp、auto_merger_policy.cpp集群统计cluster_statistics.h 与 cluster_statistics_impl.cpp测试用例同目录下的 auto_merger_policy_test.cpp、balancer_defragmentation_policy_test.cpp、balancer_chunk_selection_policy_test.cpp 与 cluster_statistics_mock.h 覆盖了三大策略的关键行为是理解判定逻辑如 3× max chunk size 阈值、25% 小块阈值、可合并块条件最快的入口。总结MongoDB 的 Balancer 远不止搬块这么简单它以双线程同步主线程 异步辅助线程为执行骨架以三种动作策略为决策中枢分别处理常规均衡、去碎片化与自动合并其每一次迁移、拆分、合并决策都建立在集群数据分布统计ClusterStatistics与集合数据量信息CollectionDataSizeInfoForBalancing之上。理解 jumbo 块的成因与forceJumbo的逃生通道、去碎片化三阶段的回退语义、自动合并的安全条件历史窗口 事务生命周期 非 jumbo以及一张参数速查表足以让运维与研发人员在生产集群中精准控制数据分布行为并为阅读和二次开发 src/mongo/db/s/balancer 目录下的源码打下坚实基础。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表