MongoDB 中的分布式集群架构
前言
前面我们了解了 MongoDB 中的索引,事务,锁等知识点。线上使用的 MongoDB 大部分的场景我们都会考虑使用分布式结构,这里我们来了解一下 MongoDB 中的分布式架构。
MongoDB 中常用的分布式架构有下面几种:
1、Replica Set 副本集模式:一个 Primary 节点用于写入数据,其它的 Secondary 节点用于查询数据,适合读写少的场景,是目前较为主流的架构方式,Primary 节点挂了,会自动从 Secondary 节点选出新的 Primary 节点,提供数据写入操作;
2、Master-Slaver 主从副本的模式:也是主节点写入,数据同步到 Slave 节点,Slave 节点提供数据查询,最大的问题就是可用性差,MongoDB 3.6
起已不推荐使用主从模式,自 MongoDB 3.2
起,分片群集组件已弃用主从复制。因为 Master-Slave
其中 Master 宕机后不能自动恢复,只能靠人为操作,可靠性也差,操作不当就存在丢数据的风险,这种模式被 Replica Set
所替代 ;
3、Sharding 分片模式:将不同的数据分配在不同的机器中,也就是数据的横向扩展,单个机器只存储整个数据中的一部分,这样通过横向增加机器的数量来提高集群的存储和计算能力。
因为 Master-Slaver
模式已经在新版本中弃用了,下面主要来介绍下 Replica Set
模式和 Sharding
模式。
Replica Set 副本集模式
MongoDB 中的 Replica Set
副本集模式,可以简单理解为一主多从的集群,包括一个主节点(primary)和多个副本节点(Secondaries)。
主节点只有一个,所有的写操作都在主节点中进行,副本节点可以有多个,通过同步主节点的操作日志(oplog)来备份主节点数据。
在主节点挂掉之后,有选举节点功能会自动从从节点中选出一个新的主节点,如果一个从节点,从节点也会自动从集群中剔除,保证集群的数据读操作不受影响。
搭建一个副本集集群最少需要三个节点:一个主节点,两个备份节点,如果三个节点分布合理,基本可以保证线上数据 99.9%
安全。
在集群只有是三个节点的情况下,当主节点超过配置的 electionTimeoutMillis
时间段(默认情况下为10 秒)内未与集合中的其他成员进行通信时,主节点就会被认为是异常了,两个副本节点也会进行选举,重新选出一个新的主节点。
在默认复制设置的情况下,从一个集群开始选举新主节点到选举完成的中间时间通常不超过 12 秒。中间包括将主节点标记不可用,发起并完成选举所需要的时间。在选举的过程中,就意味着集群暂时不能提供写入操作,时间越久集群不可写入的时间也就是越久。
关于副本节点的属性,这里来主要的介绍下:priority、hidden、slaveDelay、tags、votes。
- priority
对于副本节点,可以通过该属性增大或者减小该节点被选举为主节点的可能性,取值范围是 0-1000(如果是arbiters,则取值只有0或者1),数据越大,成为主节点的可能性越大,如果被配置为0,那么他就不能被选举成为主节点,而且也不能主动发起选举。
比如说集群中的某几台机器配置较高,希望主节点主要在这几台机器中产生,那么我们就可以通过设置 priority 的大小来实现。
- hidden
隐藏节点可以从主节点同步数据,但对客户端不可见,在mongo shell 执行 db.isMaster() 方法也不会展示该节点,隐藏节点必须Priority为0,即不可以被选举成为主节点。但是如果有配置选举权限的话,可以参与选举。
因为隐藏节点对客户端不可见,所以对于备份数据或者一些定时脚本可以直接连到隐藏节点,有大的慢查询也不会影响到集群本身对外提供的服务。
- slaveDelay
延迟从主节点同步数据,比如延迟节点时间配置为 1 小时,现在的时间是 10 点钟,那么从节点同步到的数据就是 9 点之前的数据。
隐藏节点有什么作用呢?其中有一个和重要的作用就是防止数据库误操作,比如当我们对数据的进行大批量的删除或者更新操作,为了防止出现意外,我们可能会考虑事先备份一下数据,当操作出现异常的时候,我们还能根据备份进行复原回滚操作。有了延迟节点,因为延迟节点还没及时同步到最新的数据,我们就可以基于延迟节点进行数据库的复原操作。
- tags
支持对副本集打成员标签,在查询数据时会用到,比如找到对应标签的副本节点,然后从该节点读取数据,可以根据标签对节点分类,查询数据时不同服务的客户端指定其对应的标签的节点,对某个标签的节点数量进行增加或减少,也不怕会影响到使用其他标签的服务。
- votes
表示节点是否有权限参与选举。
如何构建 MongoDB 的 Replica Set
集群,可参见 构建mongo的replica-set
副本集写和读的特性
写关注 (Write concern)
副本集写关注是指写入一条数据,主节点处理完成之后,需要其它承载副本的节点也确认写成功之后,才能给客户端返回写入数据成功。
这个功能主要是解决主节点挂掉之后,数据还没来得及同步到从节点,进而导致数据丢失的问题。
可以配置节点个数,默认配置 {“w”:1}
,这样表示主节点写入数据成功即可给客户端返回成功,“w” 配置为2,则表示除了主节点,还需要收到其中一个副本节点返回写入成功,“w” 还可以配置为 "majority",表示需要集群中大多数承载数据且有选举权限的节点返回写入成功。
比如下面的栗子,写请求里面带了 w : “majority"
,那么主节点写入完成后,数据同步到第一个副本节点,且第一个副本节点回复数据写入成功后,才给客户端返回成功。
一般有两种使用策略
1、修改副本集的配置
cfg = rs.conf()
cfg.settings.getLastErrorDefaults = { w: "majority", wtimeout: 5000 }
rs.reconfig(cfg)
2、单个数据插入或者修改的时候携带该参数
db.products.insert(
{ item: "envelopes", qty : 100, type: "Clasp" },
{ writeConcern: { w: "majority" , wtimeout: 5000 } }
)
读偏好 (Read preference)
读和写不一样, 为了保持一致,写只能通过主节点,但是读可以选择主节点,也可以选择副本节点。区别是主节点数据最新,副本节点因为同步问题可能会有延迟,但从副本节点读取数据可以分散对主节点的压力。
来看下 5 种读偏好模式的具体特点
模式 | 特点 |
---|---|
primary | 所有读请求都从主节点读取 |
primaryPreferred | 主节点正常,则所有读请求都从主节点读取,如果主节点挂掉,则从符合条件的副本节点读取 |
secondary | 所有读请求都从副本节点读取 |
secondaryPreferred | 所有读请求都从副本节点读取,但如果副本节点都挂掉了,那就从主节点读取 |
nearest | 主要看网络延迟,选取延迟最小的节点,主节点跟副本节点均可 |
Sharding 分片模式
分片将数据分布式在不同的机器中。MongoDB 使用分片来支持具有非常大数据集和高吞吐量操作的部署。
当数据量的数据逐渐变大或者数据的请求变大之后,机器我们就会考虑到升级,通常会有两种方式:垂直扩展和水平扩展。
垂直扩展:垂直扩展就是提高单个机器的性能,使用更高的 CPU,增加更多的 RAM 或者存储空间。但是单个的机器的性能总归是有上限的,因此垂直扩展理论上是有上限的。
水平扩展:水平扩展将系统数据集和负载均匀的分配到多个服务器中,根据实际的需求,增加服务器,就能提高整体的容量和性能。虽然单个机器的总速度或容量可能不高,但每台机器处理整体工作负载的一个子集,相比单一的高速大容量服务器,可能提供更好的效率。扩展部署的容量只需根据需要添加额外的服务器,与单机的高端硬件相比,这可能是更低的总体成本。代价则是增加了部署的基础设施和维护的复杂性。
分片的优势
读写
MongoDB 在分片集群上的分片上分散读写,允许每个分片处理集群操作的一部分。通过添加更多的分片,可以在集群中水平扩展读写工作负载。
对于包含分片键或符合分片键的前缀查询的操作,mongos 可以将查询定向到特定的分片或分片集,这样的查询是很高效的,如果不携带分片键,就需要将操作广播到集群中的每个分片中。
从 MongoDB 4.4
开始,mongos 可以支持对冲读取以最小化延迟,什么是对冲读,分片集群中,mongos 节点会把一个客户端的读请求同时发送给某个 Shard 分片的多个副本集节点,最后选择响应最快节点的返回结果回复给客户端,来减少业务侧感知到的延迟。
存储容量
分片在集群中的分片上分配数据,允许每个分片包含总集群数据的一个子集。随着数据集的增长,添加额外的分片可以增加集群的存储容量。
高可用性
配置服务器和分片作为副本集的部署提供了增强的可用性。
即使一个或多个分片副本集完全不可用,分片集群仍可以继续执行部分读写操作。也就是说,虽然无法访问不可用分片上的数据,但针对可用分片的读写操作仍然可以成功。
MongoDB 分片的组件
MongoDB 中的 Sharding 分片模式由下面几个组件组成:
-
分片(shard):每个分片包含一部分分片数据。每个分片可以部署为一个副本集;
-
mongos:mongos作为查询路由器,提供客户端应用程序与分片集群之间的接口。从MongoDB 4.4开始,mongos 可以支持对冲读取(hedged reads)以最小化延迟;
-
配置服务器(config servers):配置服务器存储集群的元数据和配置设置。
分片键
MongoDB 在集合级别分片数据,将收集数据分布在集群中的各个分片上。
MongoDB 使用分片键在各个分片之间分发集合中的文档。分片键由文档中的一个或多个字段组成。
在 4.2 及之前的版本中,分片集合中的每个文档都必须存在分片键字段。
从 4.4 版本开始,在分片集合中的文档可以缺少分片键字段。
可以选择集群中的分片键
在 MongoDB 4.2 及以前的版本里,一旦完成分片,分片键的选择就不能更改了。
从 MongoDB 4.4 开始,可以通过添加一个后缀字段或多个字段到现有的分片键来优化分片键。
从 MongoDB 5.0 开始,可以通过更改集合的分片键来重新分片集合。
要对集合进行分片处理,集合必须有一个以分片键开头的索引,如果集合本身没有对应的索引,在指定分片的时候会创建对应的索引。
chunk 是什么
我们知道 MongoDB 的分片集群,数据会被均匀的分布在不同的 shard 中,对于每一个 shard 中的数据,是存储在一个个的 chunk 中的,每个 chunk 代表 shard 中的一部分数据。
chunk 有什么作用:
1、Splitting:当一个 chunk 的大小超过配置的 chunk size
,MongoDB 的进程会把这个 chunk 切分成更小的 chunk ,避免 chunk 过大的情况出现;
2、Balancing:在 MongoDB 中,balancer 是一个后台的进程,负责 chunk 的转移,从而均衡各个 shard server
的负载。
简单就是使用 chunk 存储数据,方便集群进行数据在分片集群中进行数据的均衡迁移操作。
chunk 的大小选择很重要,集群默认的大小是 64 兆,可以根据业务大小进行调节。
- 较小的 chunksize
优点:数据均衡和迁移速度快,数据分布更均匀;
缺点:数据分裂频繁,路由节点消耗更多资源。
- 较大的 chunksize
优点:数据分裂少;
缺点:数据块移动集中消耗IO资源。
分片的算法
MongoDB 中支持两种分片策略来跨分片集群分布数据。哈希分片和范围分片。
哈希分片
哈希分片会计算分片键字段值的哈希,然后根据哈希分片键值,每个块分配一个范围。
虽然一系列分片键可能是“接近”的,但它们的哈希值不太可能在同一个块上。基于哈希值的数据分布有助于更加均匀地分布数据,特别是在分片键单调变化的数据集中。
对于 MongoDB 中的查询,如果查询中包含分片键,那么能定位到具体的分片直接查询,否则就要广播查询到集群中所有的分片了。
mongos 在接收到所有分片的响应后,将合并数据并返回结果文档。广播操作的性能取决于集群的整体负载,以及网络延迟、各个分片的负载和每个分片返回的文档数量等因素。所有在使用哈希分片方式部署的集群,我们应该减少可能导致广播的查询。
一般哈希对于范围查询都支持的很差。
范围分片
MongoDB 中分片键划分数会根据数据范围,将不同范围内的数据划分到不同的分片中,同一个范围内的数据就能够分布在同一个分片中了。
这样针对连续范围的查询就能够高效的执行了。
总结
1、Replica Set
副本集模式:一个 Primary 节点用于写入数据,其它的 Secondary 节点用于查询数据,适合读写少的场景,是目前较为主流的架构方式,Primary 节点挂了,会自动从 Secondary 节点选出新的 Primary 节点,提供数据写入操作;
2、Sharding 分片模式:将不同的数据分配在不同的机器中,也就是数据的横向扩展,单个机器只存储整个数据中的一部分,这样通过横向增加机器的数量来提高集群的存储和计算能力;
参考
【replication】https://www.mongodb.com/docs/manual/replication/
【MongoDB 副本集之入门篇】https://jelly.jd.com/article/5f990ebbbfbee00150eb620a
【sharding】https://www.mongodb.com/docs/manual/sharding/
【MongoDB中的分布式集群】https://boilingfrog.github.io/2023/12/16/mongo中的分布式集群/