为了账号安全,请及时绑定邮箱和手机立即绑定

搭建MongoDB集群:复制、分片与高可用性保障

在今天的世界上,数据处理能力不仅需要速度,还需要持久性及可靠性以及高可用性。在这篇博客中,我们将探讨复制分片等关键概念,以及MongoDB是如何结合这些机制来实现性能和持久性的同时保证可靠性。我们还将探讨如何配置集群以确保容错性的同时实现高可用性,并介绍手动设置MongoDB集群的实际操作。

我们为什么需要多个数据库服务器?

随着数据量的增长,我们面临的主要挑战有:性能耐久性。单个数据库服务器可能无法处理高流量或确保数据始终可用,特别是在系统出现故障时。为了解决这些问题,我们会在MongoDB集群中使用复制分片技术。

  • 耐用性 保证即使硬件出现故障也不会丢失数据。
  • 性能 使工作负载分配到多个服务器上,从而实现更快的数据读取。
复制与复制品

数据复制是MongoDB中将数据复制到多个服务器的过程,这有助于提升数据的持久性和容错能力。一个MongoDB集群通常由多个节点(服务器)组成,其中一个节点被指定为主节点(主服务器),其余节点为从节点(从服务器)。

  • 主节点:这里执行所有的写操作。
  • 从节点:这些节点会复制主节点的数据,并可用于读操作。

当主节点失败时,MongoDB 会在剩余的节点之间发起一个 选举 来选择一个新的主节点。这确保了系统自动运行,无需人工干预。

高可用性和故障容错

通过复制,MongoDB 实现了高度可用性。由于数据被复制到了多个节点,系统可以容忍故障。如果一个节点宕机了,其他节点可以接管。这种架构不仅保证了系统的 可用性,还提供了 数据持久性 的保障。

MongoDB中分片的作用是什么

虽然复制负责持久性,sharding通过将数据分布到多个节点上,提升了性能。从而使MongoDB能够通过将数据拆分到不同服务器上来水平扩展大规模数据集。

分片工作原理

在 MongoDB 中,数据分片是根据所谓的分片键(例如名称、年龄或电话号码等属性)来分布数据。这个键决定了数据如何划分到不同的节点上。

比如说:

  • 这个节点存放年龄段为:1到30岁。
  • 这个节点存放年龄段为:31到60岁。
  • 这个节点存放年龄段为:61到90岁。

分片键是根据最常查询的属性来选定的。比如说,如果用户主要是按年龄搜索,年龄就会作为分片键。

这是一个JavaScript对象,包含两个属性:名字为'Eric',年龄为30岁。

在这个例子中,如果 age 是分片键,MongoDB 会将文档存储在与 30 年龄段相关的节点上。因此,读取操作会更快,因为每个查询都会根据键被定向到相关的分片。

分片和复制的结合

仅靠分片可以提升读取性能,但它并不能确保系统的可用性和数据的持久性。为解决这一问题,MongoDB 将 分片复制 结合起来使用。每个分片都配有自己的副本集,以确保数据不会丢失且始终保持可用,即使某个分片出现故障也不受影响。

例如,我们有一个节点(节点 B),它存储了 31 到 60 岁年龄段的数据。在节点 B 的后面,有两个节点,节点 M 和节点 N,复制它的数据来确保数据的持久性。如果节点 B 出现故障,其中一个备援节点(M 或 N)可以接手。

这种shardingreplication的结合让MongoDB能够同时应对性能和数据持久性的问题,无论是在大规模部署中。

MongoDB 集群架构

1. MongoDB 路由 (mongos)

MongoDB Router (mongos) 作为客户端和分片间的桥梁。它不存储任何数据,但发挥着关键作用,根据分片键将查询和写操作引导至正确的分片。

如果没有 mongos,客户端就需要知道每份数据的位置,这会大大增加数据库操作的复杂度。mongos 处理这种复杂性,让客户端可以无缝地与集群进行交互。

2. 元数据和配置中心

这些 配置节点(元数据服务器)存储了集群架构的所有元数据,包括数据在各个分片上的分布。它们记录了数据是如何被分割的,以及每个分片包含的数据部分。

配置服务器集群非常重要,因为没有它,mongos 就无法正确地将数据路由,系统也不清楚数据在各个分片上的分布情况。

3. 分片服务器

分片是用于实际存储数据的数据库服务器。每个分片都是一个副本集,包含一个主节点和若干从节点。分片将数据分割成更小、更易管理的部分,每个分片存储一部分数据。

为什么需要分片服务器呢?

  • 数据分布:分片将数据分散到多个节点上,实现了水平扩展性。这意味着数据库可以通过增加更多的机器来扩展,而不是增大单个机器的容量。
  • 容错性和持久性:因为每个分片都是一个副本集,主节点处理写操作,从节点复制数据。如果主节点失败,选举过程会自动提升一个从节点为主节点。
  • 可扩展性:分片通过将大数据量分割成更小的部分,并分布在多个服务器上,使集群能够高效处理大量数据。这确保了即使是庞大的数据集也能被高效管理。
将这一切整合起来:MongoDB 集群的工作流程

当客户端向 MongoDB 发送请求数据时,

  1. mongos 收到请求。
  2. mongos 会查询 配置服务器 找出包含所需数据的分片。
  3. 然后把请求发送给相关的 分片服务器 处理。

在进行写操作时,mongos 将数据发送到相应的分片中的主节点。从节点复制数据以确保容错性和持久性。

MongoDB集群的实际设置

现在我们理解了MongoDB架构的组成部分,接下来我们就用Docker在AWS EC2实例上搭建一个MongoDB集群。

MongoDB 集群搭建步骤如下:

步骤1:在Amazon Linux上安装Docker容器。

第一步是安装 Docker 引擎,这样我们就可以开始搭建我们的集群了。

在终端中输入以下命令:
yum install docker -y
这将安装Docker并自动确认安装包。

步骤2:安装MongoDB工具包。

  • 要安装 mongodb-org,你首先需要添加 yum 仓库,然后创建一个 /etc/yum.repos.d/mongodb-org-7.0.repo 文件,这样你就可以通过 yum 直接安装 MongoDB 了。
[mongodb-org-7.0]  
name=MongoDB 存储库  
baseurl=https://repo.mongodb.org/yum/amazon/2023/mongodb-org/7.0/x86_64/  
gpgcheck=1  
enabled=1  
gpgkey=https://pgp.mongodb.com/server-7.0.asc  # gpgkey URL

  • 要安装最新稳定版的 MongoDB,请在终端中运行如下命令:
    sudo yum install -y mongodb-org

  • 运行一下 mongosh 命令来确认:

第三步:安装 Docker Compose。
  • 下一步是安装 docker-compose,因为我们将在一个组中分别运行分片服务器或配置服务器。
    sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose  
    sudo chmod +x /usr/local/bin/docker-compose  
    docker-compose version

命令解释:

    # 下载并安装docker-compose的最新版本到/usr/local/bin/docker-compose
    sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose  
    # 使docker-compose可执行
    sudo chmod +x /usr/local/bin/docker-compose  
    # 检查docker-compose的版本
    docker-compose version

第4步:启动配置节点。
  • 启动配置服务器的第一步是创建一个名为 configsvr.yaml 的 Docker Compose 文件,在这个文件中,我们描述每个容器的名称、使用的镜像名以及运行在容器内的命令。

    version: '3'  

    services:  

      cfgsvr1:  
        container_name: cfgsvr1  
        image: mongo  
        command: mongod --configsvr --replSet cfgrs --port 27017 --dbpath /data/db  
        ports:  
          - 40001:27017  
        volumes:  
          - cfgsvr1:/data/db  

      cfgsvr2:  
        container_name: cfgsvr2  
        image: mongo  
        command: mongod --configsvr --replSet cfgrs --port 27017 --dbpath /data/db  
        ports:  
          - 40002:27017  
        volumes:  
          - cfgsvr2:/data/db  

      cfgsvr3:  
        container_name: cfgsvr3  
        image: mongo  
        command: mongod --configsvr --replSet cfgrs --port 27017 --dbpath /data/db  
        ports:  
          - 40003:27017  
        volumes:  
          - cfgsvr3:/data/db  

    volumes:  
      cfgsvr1: {}  
      cfgsvr2: {}  
      cfgsvr3: {}
  • 该命令用来配置它们作为副本集,以确保高可用性。

  • 这些容器的端口号是 **40001, 40002, 40003** ,并暴露出来。

  • 配置服务器启动:
docker-compose -f configsvr.yaml up -d  # 使用 configsvr.yaml 配置文件启动服务

  • 尝试使用 mongosh 命令连接到一个服务器。
    mongosh <系统IP地址>(例如:192.168.1.1):40001

  • 虽然成功连接了,但在运行命令时会出现错误。
  • 这是因为这些节点互相不认识,因此即使配置为副本节点,也无法正常工作,因为它们无法参与选举,没有主从之分,任何事情都无法进行。

启动副本集:

rs.initiate(
{
  _id: "cfgrs",
  configsvr: true,
  members: [
    { _id : 0, host : "172.31.35.127:40001" },
    { _id : 1, host : "172.31.35.127:40002" },
    { _id : 2, host : "172.31.35.127:40003" }
  ]
}
)

    rs.status()

这个命令会告诉你关于副本集的全部信息,比如已添加了多少个副本,选举是否有争议,哪个节点将成为主节点(primary),哪个将成为从节点(secondary)。通常,执行这些步骤的节点将成为主节点。

第五步:设置分片服务器。
  • 我们需要再次创建一个 Docker Compose 文件 shard1svr.yaml。每个分片服务器也将有自己的副本集,以确保持久性。

    version: '3'  

    services:  

      shard1svr1:  
        container_name: shard1svr1  # 容器名称为shard1svr1
        image: mongo  
        command: mongod --shardsvr --replSet shard1rs --port 27017 --dbpath /data/db  
        ports:  
          - 50001:27017  # 端口映射为50001:27017
        volumes:  
          - shard1svr1:/data/db  # 数据卷为shard1svr1:/data/db

      shard1svr2:  
        container_name: shard1svr2  # 容器名称为shard1svr2
        image: mongo  
        command: mongod --shardsvr --replSet shard1rs --port 27017 --dbpath /data/db  
        ports:  
          - 50002:27017  # 端口映射为50002:27017
        volumes:  
          - shard1svr2:/data/db  # 数据卷为shard1svr2:/data/db

      shard1svr3:  
        container_name: shard1svr3  # 容器名称为shard1svr3
        image: mongo  
        command: mongod --shardsvr --replSet shard1rs --port 27017 --dbpath /data/db  
        ports:  
          - 50003:27017  # 端口映射为50003:27017
        volumes:  
          - shard1svr3:/data/db  # 数据卷为shard1svr3:/data/db

    volumes:  
      shard1svr1: {}  
      shard1svr2: {}  
      shard1svr3: {}  # 定义数据卷
  • 启动配置服务器,

运行 docker-compose -f shard1svr.yaml up -d.

  • 我们需要再次遵循相同的步骤,并进入其中一个分片服务器内部来启动副本集。
mongosh <系统IP地址>:50001

  • 咱们用 rs.status() 查看状态

  • 现在这是第一个分片集群,接下来我们还需要用同样的步骤创建另一个分片集群。
  • Docker Compose 文件和所需命令如下所示:

    version: '3'  

    services:  

      shard2svr1:  
        container_name: 容器名称shard2svr1  
        image: mongo  
        command: mongod --shardsvr --replSet shard2rs --port 27017 --dbpath /data/db  
        ports:  
          - 50004:27017  
        volumes:  
          - shard2svr1:/data/db  

      shard2svr2:  
        container_name: 容器名称shard2svr2  
        image: mongo  
        command: mongod --shardsvr --replSet shard2rs --port 27017 --dbpath /data/db  
        ports:  
          - 50005:27017  
        volumes:  
          - shard2svr2:/data/db  

      shard2svr3:  
        container_name: 容器名称shard2svr3  
        image: mongo  
        command: mongod --shardsvr --replSet shard2rs --port 27017 --dbpath /data/db  
        ports:  
          - 50006:27017  
        volumes:  
          - shard2svr3:/data/db  

    volumes:  
      shard2svr1: {}  
      shard2svr2: {}  
      shard2svr3: {}
    docker-compose -f shard2svr.yaml up -d

运行 shard2svr.yaml 配置文件中的 Docker Compose 服务,并将其置于后台 (-d 参数)。

    mongosh <系统IP地址>:50004 (启动MongoDB shell连接到指定的系统IP地址和端口)
    rs.initiate(  
      {  
        _id: "shard2rs",  
        members: [  
          { _id : 0, host : "172.31.35.127:50004" },  
          { _id : 1, host : "172.31.35.127:50005" },  
          { _id : 2, host : "172.31.35.127:50006" }  
        ]  
      }  
    )  

    rs.status()
第六步:启动 MongoDB 路由器程序 (mongos):

下一步是设置mongos路由,它会将客户端请求导向合适的分片。

  • 编写一个供 mongos 服务器使用的 Docker Compose 文件。

    version: '3'

    services:

      mongos:
        container_name: mongos
        image: mongo
        command: mongos --configdb cfgrs/172.31.35.127:40001,172.31.35.127:40002,172.31.35.127:40003 --bind_ip 0.0.0.0 --port 27017
        ports:
          - 60000:27017

在这条命令里:

  • mongos 是路由器服务。
  • --configdb 参数设置了配置服务器副本集及其服务器地址。
  • 启动 mongos
     docker-compose -f mongos.yaml up -d # 启动 MongoDB 服务器(守护进程模式)

  • 请连接到 mongos 服务器。

sh.status()

这个命令用来检查分片的状态信息。

  • 现在没有可用的分片了,所以我们得添加一些。
// 添加碎片 "shard1rs/172.31.35.127:50001,172.31.35.127:50002,172.31.35.127:50003"
sh.addShard("shard1rs/172.31.35.127:50001,172.31.35.127:50002,172.31.35.127:50003")

  • 现在在 shards 列中增加了一个分片。
  • 同样,我们也将增加第二个分片群。

第 5 步:测试一下设置:

在用户数据库中开启分片功能。

启用分片 "userdb"

  • 在数据库启用分片后,我们也需要在集合启用分片,否则数据将不会按照预期分布。为此,我们可以通过以下命令来创建集合。

使用sh.shardCollection命令(将'userdb.persons'集合根据'age'字段使用哈希算法进行分片)。

  • 在这里,userdb 是我们的数据库,persons 是我们的集合,age 是我们的分片关键字,而 hashed 是一种用于将数据根据不同分片存储的算法。

我们来看看 persons 这个集合的分片分布情况,看看每个分片里存了多少数据。

执行这个命令来获取人员的分片分布: db.persons.getShardDistribution()

  • 在存储数据方面,你会看到这里的一些变化,这会告诉你数据将存放在哪个位置。
将一个名为 "Harsh",年龄为 20 的人插入到 persons 集合中。

db.persons.insert({name: "Harsh", age: 20})

将{name: 'Jack', age: 11}插入到persons集合中

db.persons.insert({name: "Jack", age: 11})

  • 从这些值中,我们知道两个文档都存储在shard2中。
  • 我们现在插入一个文档吧。
将{name: 'Jack', age: 5}这个对象插入到persons集合中

这里的数据显示是存储在 shard1 中,这些决策是根据分片键 age 来决定的。

最后

MongoDB的集群架构——包括mongos路由服务器配置服务器分片服务器——设计用于在大规模情况下提供高性能、高可用性和容错性。通过将数据分布在多个分片上并在节点之间复制数据,MongoDB确保系统既可扩展又持久可靠。

分片提升性能,复制增强容错性的结合让MongoDB成为处理大规模的、关键任务应用的强大解决方案。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消