Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Mongos 与集群均衡

Mongos 与集群均衡

作者头像
孔德雨
修改于 2017-06-19 11:27:11
修改于 2017-06-19 11:27:11
12.6K20
代码可运行
举报
文章被收录于专栏:孔德雨的专栏孔德雨的专栏
运行总次数:0
代码可运行

mongodb 可以以单复制集的方式运行,client 直连 mongod 读取数据。

单复制集的方式下,数据的水平扩展的责任推给了业务层解决(分实例,分库分表),mongodb 原生提供集群方案,该方案的简要架构如下:

mongodb集群是一个典型的去中心化分布式集群。mongodb集群主要为用户解决了如下问题:

  • 元数据的一致性与高可用(Consistency + Partition Torrence)
  • 业务数据的多备份容灾(由复制集技术保证)
  • 动态自动分片
  • 动态自动数据均衡

下文通过介绍 mongodb 集群中各个组成部分,逐步深入剖析 mongodb 集群原理。

ConfigServer

mongodb 元数据全部存放在configServer中,configServer 是由一组(至少三个)MongoDb实例组成的集群。

ConfigServer 的唯一功能是提供元数据的增删改查。和大多数元数据管理系统(etcd,zookeeper)类似,也是保证一致性与分区容错性。本身不具备中心化的调度功能。

ConfigServer与复制集

ConfigServer 的分区容错性(P)和数据一致性(C)是复制集本身的性质。

mongodb 的读写一致性由 WriteConcern 和 ReadConcern 两个参数保证。

writeConcern

readConcern

两者组合可以得到不同的一致性等级。

指定 writeConcern:majority 可以保证写入数据不丢失,不会因选举新主节点而被回滚掉。

readConcern:majority + writeConcern:majority 可以保证强一致性的读

readConcern:local + writeConcern:majority 可以保证最终一致性的读

mongodb 对configServer全部指定writeConcern:majority 的写入方式,因此元数据可以保证不丢失。

对 configServer 的读指定了 ReadPreference:PrimaryOnly 的方式,在 CAP 中舍弃了A与P得到了元数据的强一致性读。

Mongos

数据自动分片

对于一个读写操作,mongos 需要知道应该将其路由到哪个复制集上,mongos通过将片键空间划分为若干个区间,计算出一个操作的片键的所属区间对应的复制集来实现路由。

Collection1 被划分为4个chunk,其中

chunk1 包含(-INF,1) , chunk3 包含[20, 99) 的数据,放在shard1上。

chunk2 包含 [1,20), chunk4 包含[99, INF) 的数据,放在shard2上。

chunk 的信息存放在configServer 的mongod实例的 config.chunks 表中,格式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{   
    "_id" : "mydb.foo-a_\"cat\"",   
    "lastmod" : Timestamp(1000, 3),  
    "lastmodEpoch" : ObjectId("5078407bd58b175c5c225fdc"),   
    "ns" : "mydb.foo",   
    "min" : {         "animal" : "cat"   },   
    "max" : {         "animal" : "dog"   },   
    "shard" : "shard0004"
}

值得注意的是:chunk是一个逻辑上的组织结构,并不涉及到底层的文件组织方式。

启发式触发chunk分裂

mongodb 默认配置下,每个chunk大小为16MB。超过该大小就需要执行chunk分裂。chunk分裂是由mongos发起的,而数据放在mongod处,因此mongos无法准确判断每个增删改操作后某个chunk的数据实际大小。因此mongos采用了一种启发式的触发分裂方式:

mongos在内存中记录一份 chunk_id -> incr_delta 的哈希表。

对于insert和update操作,估算出incr_delta的上界(WriteOp::targetWrites), 当incr_delta超过阈值时,执行chunk分裂。 值得注意的是:

1) chunk_id->incr_delta 是维护在mongos内存里的一份数据,重启后丢失 2) 不同mongos之间的这份数据相互独立 3) 不带shardkey的update 无法对 chunk_id->incr_delta 作用

因此这个启发式的分裂方式很不精确,而除了手工以命令的方式分裂之外,这是mongos自带的唯一的chunk分裂方式。

chunk分裂的执行过程

1) 向对应的mongod 发起splitVector 命令,获得一个chunk的可分裂点 2) mongos 拿到这些分裂点后,向mongod发起splitChunk 命令

splitVector执行过程:

1) 计算出collection的文档的 avgRecSize= coll.size/ coll.count 2) 计算出分裂后的chunk中,每个chunk应该有的count数, split_count = maxChunkSize / (2 * avgRecSize) 3) 线性遍历collection 的shardkey 对应的index的 [chunk_min_index, chunk_max_index] 范围,在遍历过程中利用split_count 分割出若干spli

splitChunk执行过程:

1) 获得待执行collection的分布式锁(向configSvr 的mongod中写入一条记录实现) 2) 刷新(向configSvr读取)本shard的版本号,检查是否和命令发起者携带的版本号一致 3) 向configSvr中写入分裂后的chunk信息,成功后修改本地的chunk信息与shard的版本号 4) 向configSvr中写入变更日志 5) 通知mongos操作完成,mongos修改自身元数据

chunk分裂的执行流程图:

问题与思考

问题一:为何mongos在接收到splitVector的返回后,执行splitChunk 要放在mongod执行而不是mongos中呢,为何不是mongos自己执行完了splitChunk再通知mongod 修改元数据?

我们知道chunk元数据在三个地方持有,分别是configServer,mongos,mongod。如果chunk元信息由mongos更改,则其他mongos与mongod都无法第一时间获得最新元数据。可能会发生这样的问题,如下图描述:

Mongos对元数据的修改还没有被mongod与其他mongos感知,其他mongos与mongod的版本号保持一致,导致其他mongos写入错误的chunk。

如果chunk元信息由mongod更改,mongod 先于所有的mongos感知到本shard的元数据被更改,由于mongos对mongod的写入请求都会带有版本号(以发起者mongos的POV 持有的版本号),mongod发现一个读写带有的版本号低于自身版本号时就会返回 StaleShardingError,从而避免对错误的chunk进行读写。

Mongos对读写的路由

读请求:

mongos将读请求路由到对应的shard上,如果得到StaleShardingError,则刷新本地的元数据(从configServer读取最新元数据)并重试。

写请求:

mongos将写请求路由到对应的shard上,如果得到StaleShardingError,并不会像读请求一样重试,这样做并不合理,截至当前版本,mongos也只是列出了一个TODO(batch_write_exec.cpp:185)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
185          // TODO: It may be necessary to refresh the cache if stale, or maybe just
186          // cancel and retarget the batch

chunk迁移

chunk迁移由balancer模块执行,balancer模块并不是一个独立的service,而是mongos的一个线程模块。同一时间只有一个balancer模块在执行,这一点是mongos在configServer中注册分布式锁来保证的。

balancer 对于每一个collection的chunk 分布,计算出这个collection需要进行迁移的chunk,以及每个chunk需要迁移到哪个shard上。计算的过程在BalancerPolicy 类中,比较琐碎。

chunk迁移.Step1

MigrationManager::scheduleMigrations balancer对于每一个collection,尝试获得该collection的分布式锁(向configSvr申请),如果获得失败,表明该collection已有正在执行的搬迁任务。这一点说明对于同一张表统一时刻只能有一个搬迁任务。如果这张表分布在不同的shard上,完全隔离的IO条件可以提高并发,不过mongos并没有利用起来这一点。

如果获得锁成功,则向源shard发起moveChunk 命令

chunk迁移.Step2

mongod 执行moveChunk命令

cloneStage

1) 源mongod 根据需要迁移的chunk 的上下限构造好查询计划,基于分片索引的扫描查询。并向目标mongod发起recvChunkStart 指令,让目标chunk 开始进入数据拉取阶段。

2) 源mongod对此阶段的修改, 将id字段buffer在内存里(MigrationChunkClonerSourceLegacy类),为了防止搬迁时速度过慢buffer无限制增长,buffer大小设置为500MB,在搬迁过程中key的更改量超过buffer大小会导致搬迁失败。

3) 目标mongod 在接收到recvChunkStart命令后

a. 基于chunk的range,将本mongod上的可能脏数据清理掉 b. 向源发起_migrateClone指定,通过1)中构造好的基于分配索引的扫描查询得到该chunk 数据的snapshot c. 拷贝完snapshot后,向源发起_transferMods命令,将2)中维护在内存buffer中的修改 d. 源在收到_transferMods后,通过记录的objid查询对应的collection,将真实数据返回给目标。 e. 目标在收完_transferMods 阶段的数据后,进入steady状态,等待源接下来的命令。这里有必要说明的是:用户数据源源不断的写入,理论上_transferMods 阶段会一直有新数据,但是必须要找到一个点截断数据流,将源的数据(搬迁对应的chunk的数据)设置为不可写,才能发起路由更改。因此这里所说的“_transferMods阶段的所有数据”只是针对于某个时间点,这个时间点过后依然会有新数据进来。 f. 源心跳检查目标是否已经处于steady状态,如果是,则封禁chunk的写入,向目标发起_recvChunkCommit命令,之后源的chunk上就无修改了。 g. 目标收到_recvChunkCommit命令后,拉取源chunk上的修改并执行,执行成功后源解禁路由并清理源chunk的数据

流程图如下:

总结

经过分析,我们发现Mongos在迁移方面有很大的待提升空间

1) 一张表同一时间只能有一个chunk在搬迁,没有充分利用不同机器之间的IO隔离来做并发提速。 2) 搬迁时需要扫描源的数据集,一方面会与业务争QPS,一方面会破坏(如果是Mmap引擎)热点读写的working-set 3) Mongos启发式分裂chunk的方式极不靠谱,mongos重启后,启发信息就丢失了,而且部分常见的写入模式也不会记录启发信息

经过CMongo团队的测试,mongos自带的搬迁方案处理100GB的数据需要33小时。CMongo团队分析了mongos自带的搬迁方案的缺陷,自研了一套基于备份的搬迁方案,速度有30倍以上的提升,敬请期待!

相关推荐

MongoDB复制集原理

MongoDb Mmap引擎分析

MongoDB安装部署与高可用

评论
登录后参与评论
2 条评论
热度
最新
数据库设计成去中心化,有什么好处?
数据库设计成去中心化,有什么好处?
回复回复点赞举报
学习了学习了
学习了学习了
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
MongoDB从入坑到入迷
背景:我司是一家正处于高速发展,目前拥有数百万用户,年销售额近五十亿的社交电商公司。公司技术部建立之初,为了适应用户量的高速增长,与业务的不断变更迭代,在选用数据库的时候,经过调研对比我们选择了MongoDB。
MongoDB中文社区
2020/06/16
1K0
MongoDB 分片集群技术
---- 在了解分片集群之前,务必要先了解复制集技术! ----  1.1 MongoDB复制集简介   一组Mongodb复制集,就是一组mongod进程,这些进程维护同一个数据集合。复制集提供了数据冗余和高等级的可靠性,这是生产部署的基础。 1.1.1 复制集的目的   保证数据在生产部署时的冗余和可靠性,通过在不同的机器上保存副本来保证数据的不会因为单点损坏而丢失。能够随时应对数据丢失、机器损坏带来的风险。   换一句话来说,还能提高读取能力,用户的读取服务器和写入服务器在不同的地方,而且,由不同的
惨绿少年
2018/03/30
2.5K0
MongoDB 事务 — 基础入门篇
MongoDB 单文档原生支持原子性,也具备事务的特性,但是我们说起事务,通常是指在多文档中的实现,因此,MongoDB 在 4.0 版本支持了多文档事务,4.0 对应于复制集的多表、多行,后续又在 4.2 版本支持了分片集的多表、多行事务操作。
五月君
2020/02/26
2.8K0
Change Stream源码解读
MongoDB从3.6开始推出了Change Stream功能,提供实时的增量数据流功能,为同步、分析、监控、推送等多种场景使用带来福音。4.0中引入的混合逻辑时钟,可以支持分片集群在不关闭balancer的情况下,吐出的增量数据在即使发生move chunk发生的情况下,还能够保证数据的因果一致性。不但如此,随着4.0.7开始推出的High Water Mark功能,使得返回的change stream cursor包括Post Batch Resume Token,更好的解决Change Stream中ResumeToken推进的问题。关于Change Stream的功能解读,网上可以找到比较多的资料,比如张友东的这篇解读介绍了Change Stream与oplog拉取的对比以及基本的使用。本文将主要侧重从内核源码层面进行解读,主要介绍分片集群版下Change Stream在mongos和mongod上都执行了哪些操作。此外,由于4.0开始MongoDB使用了混合逻辑时钟,从而保证了move chunk的因果一致性,所以本文还会先简单介绍一下MongoDB中混合逻辑时钟的原理。
MongoDB中文社区
2020/11/02
2.5K0
Change Stream源码解读
万亿级MongoDB集群的路由优化之路
本文为2020年MongoDB应用案例与解决方案征集活动优秀应用案例:MongoDB在七牛云的应用,作者李鑫。
MongoDB中文社区
2021/01/28
1K0
MongoDB 第一期 :集群搭建
本文主要介绍了如何基于MongoDB搭建高可用集群,包括集群的搭建步骤、配置文件参数解析、集群的监控方式以及如何提高集群的可用性。通过实际例子讲解了如何快速搭建一个高可用的MongoDB集群。
迪B哥
2017/07/06
2K0
MongoDB 第一期 :集群搭建
Mongodb分片集群部署
对于单台数据库服务器,庞大的数据量及高吞吐量的应用程序对它而言无疑是个巨大的挑战。频繁的CRUD操作能够耗尽服务器的CPU资源,快速的数据增长也会让硬盘存储无能为力,最终内存无法满足数据需要导致大量的I/O,主机负载严重。为了解决这种问题,对于数据库系统一般有两种方法:垂直扩展和分片(水平扩展)。
拓荒者
2019/09/10
2K0
Mongodb分片集群部署
MongoDB 分片
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
为为为什么
2024/09/28
1670
MongoDB 分片
一文读懂MongoDB chunk 迁移
MongoDB在Sharding模式下(对于Sharding不了解的可以参考shard介绍),通过Mongos向开启了shard分片的集合写入文档,这些文档会根据其shardKey分散到MongoDB的不同shard上。
MongoDB中文社区
2021/03/01
2.4K0
带着问题学习分布式系统之数据分片
分布式系统(尤其是分布式存储系统)需要解决的两个最主要的问题,即数据分片和数据冗余,下面这个图片形象生动的解释了其概念和区别: 其中数据即A、B属于数据分片,原始数据被拆分成两个正交子集分布在两个节点
用户1263954
2018/01/30
1.8K0
带着问题学习分布式系统之数据分片
mongoDB知识总结
MongoDB 是基于文档的 NoSql 存储引擎。MongoDB 的数据库管理由数据库、Collection(集合,类似MySql的表)、Document(文档,类似MySQL的行)组成,每个Document都是一个类JSON结构BSON结构数据。 MongoDB 的核心特性是:No Schema、高可用、分布式(可平行扩展),另外MongoDB自带数据压缩功能,使得同样的数据存储所需的资源更少。
leobhao
2024/04/01
4280
mongoDB知识总结
MongoDB运维与开发(10)---chunk
MongoDB中,在使用到分片的时候,常常会用到chunk的概念,chunk是指一个集合数据中的子集,也可以简单理解成一个数据块,每个chunk都是基于片键的范围取值,区间是左闭右开。例如,我们的片键是姓名的第二个字母,包含了A-Z这26中可能,理想情况下,划分为26个chunk,其中每个字母开头的姓名记录即为一个chunk。
AsiaYe
2020/12/14
7620
MongoDB运维与开发(10)---chunk
MongoDB分片集群原理、搭建及测试详解
随着技术的发展,目前数据库系统对于海量数据的存储和高效访问海量数据要求越来越高,MongoDB分片机制就是为了解决海量数据的存储和高效海量数据访问而生。 MongoDB分片集群由mongos路由进程(轻量级且非持久化进程)、复制集组成的片shards(分片一般基于复制集故障转移和冗余备份功能)、一组配置服务器(存储元数据信息,一般冗余3台)构成。
用户7353950
2022/06/23
1.2K0
《一起学mongodb》之第三卷分片集群
上一篇介绍了 mongo 的三种部署方式,「单点、主从、副本集」三种部署方式,今天就跟大家聊聊最后一种「分片集群」的方式,分片集群也是 mongo 能够作为万亿级别数据库的核心魅力所在,也有一句话说到:
moon聊技术
2022/02/17
5590
《一起学mongodb》之第三卷分片集群
Mongodb7.0.14集群分片部署
MongoDB 集群分片是一种水平扩展数据库的方法,通过将数据分布在多个物理服务器上,提高系统的性能和可扩展性。分片的核心思想是将数据分成多个部分(称为“分片”),每个分片存储在不同的服务器上,从而分散负载,提高查询和写入性能。
DBA实战
2024/10/10
2570
Mongodb7.0.14集群分片部署
shardCollection源码解析
在阅读源码之前,MongoDB shardCollection就像一个黑盒子,让人很难窥其内貌,在运营过程中遇到的很多问题都难以抓住关键点。本文以“shardCollection超时问题”为入口,探讨下面4个核心问题:
MongoDB中文社区
2020/11/02
1K0
shardCollection源码解析
MongoDB 之chunk分裂之autosplit
在MongoDB分片集群中,使用分片键将数据分割成连续的数据块,这种数据块称之为chunk。
AsiaYe
2021/07/14
1.5K0
MongoDB 之chunk分裂之autosplit
《一起学mongodb》之第五卷 事务
事务是 mongoDB 中非常核心的一个功能,在 4.0 版本以前,mongoDB 只支持单个文档的事务,在 4.0 和 4.2 版本之后,分别支持了复制集事务和分片事务,也可以说在大多数的数据库中都是非常重要的一个功能,值得我们单独拉一章去讲解
moon聊技术
2022/04/08
5990
《一起学mongodb》之第五卷 事务
MongoDB 路由表刷新导致响应慢场景解读
MongoDB sharding 实例从3.4版本升级到 4.0版本 以后插入性能明显降低,观察日志发现大量的 insert 请求慢日志:
MongoDB中文社区
2020/11/11
2K0
MongoDB 路由表刷新导致响应慢场景解读
Shard 分片集群
要构建一个 MongoDB Sharding Cluster,需要三种角色:
MonroeCode
2018/01/12
1.7K0
相关推荐
MongoDB从入坑到入迷
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验