MongoDB 高可用集群知识小锦囊 - 脑瓜子转动篇

2 篇文章 0 订阅
2 篇文章 0 订阅

MongoDB 副本集是通过什么方式同步数据呢?

MongoDB 副本集数据同步所依赖的核心是 Oplog,它就像 MySQL 中的 Binlog 一样,记录着主节点上执行的每一个操作!而 Secondary 通过复制 Oplog 并应用的方式来进行数据同步。

Oplog 就是一个大小固定、循环复用的日志文件(默认分配 5% 的可用空间,即64位),当 Secondary 落后 Primary 很多,直到 oplog 被复写,那只能重新全量同步,而拉取全量同步代价特别高,直接影响 Primary 的读写性能

我们可以用 –oplogSize 选项指定具体 Oplog 大小,设置合适的大小在生产应用中是非常重要的一个环节。

MongoDB 副本集是实时同步吗?

这其实也是在问数据库一致性的问题:

  • MySQL 的半同步复制模式保证数据库的强一致
  • Oracle DataGuard 的最大保护模式也能够保证数据库的强一致
  • 而 MongoDB 可以通过 getLastError 命令来保证写入的安全,但其毕竟不是事务操作,无法做到数据的强一致。

MongoDB 副本集 Secondary 通常会落后几毫秒,如果有加载问题、配置错误、网络故障等原因,延迟可能会更大。

MongoDB 副本集是如何进行选举的?

选举我们可以简单理解为如何从集群节点中选择合适的节点提升为 Primary 的过程。跟很多 NoSQL 数据库一样,MongoDB 副本集采用的是 Bully 算法

大致思想是集群中每个成员都能够声明自己是主节点并通知到其他节点,被其他节点接受的节点才能成为主节点。

MongoDB 副本集有着 “大多数” 的概念,在进行选举时必须遵循 “大多数” 规则,节点在得到大多数支持时才能成为主节点,而副本集中节点存活数量必须大于 “大多数” 的数量

下列列举一些成员总数选举需满足的 “大多数” 数量对应关系

成员总数大多数
11
22
32
43
53
64
74
85
95
106
116
127

💡 即:需要的大多数节点数量计算为 majority = (n / 2) + 1

MongoDB 什么情况下会触发选举?

  1. 初始化副本集时;
  2. 备份节点无法和主节点通讯时(可能主节点宕或网络原因);
  3. Primary 手动降级,rs.stepDown(sec),默认 60s

选举步骤

  1. 获取每个服务器节点的最后操作时间戳。每个 mongodb 都有 oplog 机制会记录本机的操作,方便和主服务器进行对比数据是否同步还可以用于错误恢复;
  2. 如果集群中大部分服务器宕机了,保留活着的节点都为 secondary 状态并停止选举;
  3. 如果集群中选举出来的主节点或者所有从节点最后一次同步时间看起来很旧了,停止选举等待人工操作;
  4. 如果上面都没有问题就选择最后操作时间戳最新(保证数据最新)的服务器节点作为主节点。

MongoDB 副本集成员节点数量为偶数个会有问题吗?

❓ 有些人可能在设计 MongoDB 副本集架构过程中会产生成员节点必须是奇数个的误区,让我们来看看为啥?

nl01

以该图为例子,现在成员节点有 4 个,包含了 1 个 Primary 和 3 个 Secondary 节点,假如 Primary 因为服务器异常宕机了,那么还剩余 3 个节点可以选举,只要满足 “大多数” 2个即可以选出新的 Primary

因此,在单机房内不管副本集成员节点数为偶数还是奇数都是没有问题的!

为啥强调是 “单机房” 内呢?

nl2

以上图为例子,节点分布两个机房 IDC1 和 IDC2,每个机房的成员节点数量一致,在两个机房之间心跳中断时,整个集群就会出现无法选举 Primary 的问题,这就是 MongoDB 副本集中的脑裂。

还有另外的一种“脑裂”情况如下,当两个机房的网络通讯中断,而机房内的成员节点能满足 “大多数” 的选举规则的时候,就会变成两个独立的 MongoDB 副本集群!

nl3

如何防止脑裂?

方案一:大多数成员在一个中心

nl4

需求: 副本集的 Primary 总在主数据中心
缺点:如果主数据中心挂掉了,就没有可用的 Primary 节点了

方案二:引入独立仲裁节点

nl5

需求:跨机房容灾
缺点:额外需要第三个机房

分片的数据是如何存储的?

Chunk 是什么?

在一个 shard server 内部,MongoDB 还会把数据分为 chunks,每个 chunk 代表这个 shard server 内部一部分数据。chunk 的产生,会有以下两个用途:

  1. Splitting : 当一个 chunk 的大小超过配置中的 chunk size 时,MongoDB 的后台进程会把这个 chunk 切分成更小的 chunk,从而避免 chunk 过大的情况
  2. Balancing : 在 MongoDB 中,balancer 是一个后台进程,负责 chunk 的迁移,从而均衡各个 shard server 的负载,系统初始 1 个 chunkchunk size 默认值 64M,生产库上选择适合业务的 chunk size 是最好的。MongoDB 会自动拆分和迁移 chunks

分片集群的数据分布(shard 节点)

  • 使用 chunk 来存储数据
  • 进群搭建完成之后,默认开启一个 chunk,大小是 64M,范围为 [minKey, maxKey]
  • 存储需求超过 64Mchunk 会进行分裂,如果单位时间存储需求很大,设置更大的 chunk
  • chunk 会被自动均衡迁移

chunk分裂及迁移

chunk 分裂的时机:在插入和更新,读数据不会分裂。

随着数据的增长,其中的数据大小超过了配置的 chunk size ,默认是 64M ,则这个 chunk 就会分裂成两个。数据的增长会让 chunk 分裂得越来越多。

chunk01

这时候,各个 shard 上的 chunk 数量就会不平衡。这时候,mongos 中的 balancer 组件(参考官网说明) 就会执行自动平衡。把 chunkchunk 数量最多的 shard 节点挪动到数量最少的节点。

chunk02

块迁移阈值

为了最大限度地减少平衡对集群的影响, 平衡器 balancer 仅在分片集合的块分布达到特定阈值后才开始平衡。 阈值适用于具有最多集合块的分片与具有最少集合块的分片之间的块数量差异。平衡器具有以下阈值:

chunk 数量迁移阈值
少于 20 个2
20 ~ 794
80 及以上8

🟡 ️当该集合的任意两个分片上的块数之差小于2或块迁移失败时,平衡器将停止在目标集合上运行。

块迁移过程

  • 平衡器 balancer 进程将 moveChunk 命令发送到源分片。
  • 源通过内部命令开始移动 moveChunk 。在迁移过程中,对块的操作会路由到源分片。源分片负责传入的块写入操作。
  • 目标分片构建源所需但目标上不存在的任何索引。
  • 目标分片开始请求块中的文档并开始接收数据的副本。
  • 接收到块中的最终文档后,目标分片启动同步过程,以确保它具有迁移过程中发生的迁移文档的更改。
  • 完全同步后,源分片连接到配置数据库 config server 并使用块的新位置更新集群元数据。
  • 源分片完成元数据更新后,一旦块上没有打开的游标,源分片就会删除其文档副本。

Chunk Size 应该如何选择呢?

适合业务的chunksize是最好的。

chunk 的分裂和迁移非常消耗 IO 资源;因此 chunkSize 的选择对分裂及迁移的影响是巨大的!

  • MongoDB 默认的 chunkSize64MB,如无特殊需求,建议保持默认值chunkSize 会直接影响到 chunk 分裂、迁移的行为。
  • chunkSize 越小,chunk 分裂及迁移越多越快,数据分布越均衡,而且路由节点会消耗更多的资源;反之,chunkSize 越大,chunk 分裂及迁移会更少,但可能导致数据分布不均,IO消耗也高。
  • chunkSize 太小,容易出现 jumbo chunk(即 shardKey 的某个取值出现频率很高,这些文档只能放到一个 chunk 里,无法再分裂)而无法迁移;chunkSize 太大,则可能出现 chunk 内文档数太多(chunk 内文档数不能超过 250000 )而无法迁移
  • chunk 自动分裂只会在数据写入时触发,所以如果将 chunkSize 改小,系统需要一定的时间来将 chunk 分裂到指定的大小。
  • chunk 只会分裂,不会合并,所以即使将 chunkSize 改大,现有的 chunk 数量不会减少,但 chunk 大小会随着写入不断增长,直到达到目标大小。
chunksizesplitting次数跨分片数数据均匀网络传输次数迁移次数单次迁移传输量查询速度
变大减少变少不太均匀变少变少变大变快
变小增多变多更均匀变多变多变小变慢

MongoDB 中 Collection 的数据是根据什么进行分片的呢?

分片键(Shard key)

📕 分片键就是在集合中选一个字段或者组合字段,用该键的值作为数据拆分的依据。

分片键必须满足如下条件:

  • 分片键是不可变(指数据里面的分片键字段值不能被更新)
  • 分片键必须有索引,而且是升序的
  • 分片键大小限制 512bytes
  • 分片键用于路由查询,如果查询语句控制不当(比如不通过 _id 或者分片键的单挑查询会出问题)
  • 键的文档(也不支持空值插入)

当集合不存在的情况下,使用 sh.shardCollection 命令来分片集合会自动创建索引!

一个自增的分片键对写入和数据均匀分布就不是很好,因为自增的片键总会在一个分片上写入(因为新的文档总是被添加到当前具有最大值的分片上),后续达到某个阀值可能会写到别的分片。但是按照片键查询会非常高效。

哈希分片(Hash Sharding)

🔖 分片过程中利用哈希索引作为分片,基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。(官网说明入口)

对于基于哈希的分片,MongoDB 计算字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有相近分片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。

shard01

注意:MongoDB 4.4及以上版本可以支持 复合索引字段 进行哈希分片。

范围分片(range sharding)

🔖 基于范围的分片涉及将数据划分为由分片键值确定的连续范围。在此模型中,具有近似分片键值的文档可能位于同一块或分片中。这允许高效查询,读取连续范围内的目标文档。然而,由于分片键选择不当,读取和写入性能可能会降低。(官网说明入口)

shard02

如果没有手动指定分片方法的情况下,基于范围的分片(range sharding) 是默认的分片方法

哈希和范围的结合

如下是基于 X 索引字段进行范围分片,但是随着 X 的增长,大于 20 的数据全部进入了 Chunk C, 这导致了数据的不均衡。

shard03

这时对 X 索引字段建哈希索引,可以使数据均匀分配:

shard04

通过 Zone 来管理分片数据分配

🔖 在分片集群中,您可以创建代表一组分片的区域,并将一个或多个分片键值范围与该区域相关联。MongoDB 仅将属于区域范围的读取和写入路由到区域内的那些分片。(官网说明入口)

应用区域(zone)的一些常见部署模式如下:

  • 将指定的数据放在指定的分片上。
  • 确保最相关的数据驻留在地理上最靠近应用程序服务器的分片上。
  • 根据分片硬件的配置/性能将数据路由到分片。

下图说明了具有三个分片和两个区域的分片集群。

  • A区域代表下边界为 1 且上限为 10 的范围。
  • B区域代表下边界为 10 且上限为 20 的范围。
  • 分片 AlphaBeta 具有 A 区域 和 B 区。分片 Charlie 没有与之关联的区域。 群集处于稳定状态。

shard05

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不认输的猴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值