分步指南:部署基本 MongoDB 分片集群

部署一个包含配置服务器、分片副本集、mongos 路由器和分片验证的基本 MongoDB 分片集群。

分步指南:部署基本 MongoDB 分片集群

MongoDB 是一款流行的 NoSQL 文档数据库,在处理大量数据时具有高性能和灵活性。然而,随着数据增长,单个服务器或副本集可能达到其扩展极限。这时分片技术便发挥作用,通过将数据分布到多个服务器(即分片)来实现水平扩展。

本指南将引导您在本地主机上搭建一个基本的 MongoDB 分片集群以进行学习。您将配置配置服务器、分片副本集和 mongos 路由器,然后为集合启用分片。

理解 MongoDB 分片集群

MongoDB 分片集群由三个主要组件组成,它们协同工作以分发和路由数据:

  • 分片副本集:这些是实际承载数据的节点。每个分片都是一个副本集,以提供高可用性和数据冗余。数据在这些分片之间进行分区。
  • 配置服务器:这些服务器存储集群的元数据,包括数据块到分片的映射。从 MongoDB 3.2 开始,配置服务器必须部署为副本集(CSRS - 配置服务器副本集),以实现高可用性和一致性。
  • mongos 路由器:这些充当查询路由器,为客户端应用程序提供接口。mongos 实例根据集群的元数据将客户端操作定向到适当的分片。应用程序连接到 mongos,而不是直接连接到分片。

MongoDB 分片集群架构图(概念) MongoDB 分片集群的概念图(图片来源:MongoDB 官方文档)

前提条件

在开始之前,请确保您具备以下条件:

  1. 多台机器/虚拟机:对于真正分布式的分片集群,您至少需要 6-9 台机器/虚拟机/Docker 容器。在本基础教程中,我们可以使用不同端口在单台机器上模拟,但请记住生产环境需要专用资源。
    • 3 台用于配置服务器(configSrv01、configSrv02、configSrv03)
    • 每个分片至少 2-3 台(例如,Shard01-RS01、Shard01-RS02、Shard01-RS03;Shard02-RS01、...)
    • 1 台以上用于 mongos 路由器
  2. MongoDB 安装:在每台将运行 mongodmongos 的机器上安装受支持的 MongoDB 服务器版本。使用 mongosh 执行 shell 命令。
  3. 网络:确保所有机器能在必要端口上相互通信(配置服务器、分片和 mongos 的默认端口分别为 27017270182701927020,或使用自定义端口)。
  4. 目录结构:为每个 mongodmongos 实例创建专用的数据和日志目录。

为简化本指南,我们将使用 localhost 和不同端口及目录。在生产环境中,您应使用实际的主机名或 IP 地址。

推荐的目录结构(本地主机设置示例)

mkdir -p /data/db/configdb01 /data/db/configdb02 /data/db/configdb03
mkdir -p /data/db/shard01-rs01 /data/db/shard01-rs02 /data/db/shard01-rs03
mkdir -p /data/db/shard02-rs01 /data/db/shard02-rs02 /data/db/shard02-rs03
mkdir -p /data/log/config /data/log/shard01 /data/log/shard02 /data/log/mongos

部署步骤

步骤 1:设置配置服务器(配置副本集)

配置服务器存储分片集群的元数据。它们必须作为副本集运行。

  1. 启动配置服务器的 mongod 实例:每个实例需要 --configsvr--replSet 选项。

    # 配置服务器 1
    mongod --configsvr --replSet cfgReplSet --dbpath /data/db/configdb01 --port 27019 --bind_ip localhost --logpath /data/log/config/configdb01.log --fork
    
    # 配置服务器 2
    mongod --configsvr --replSet cfgReplSet --dbpath /data/db/configdb02 --port 27020 --bind_ip localhost --logpath /data/log/config/configdb02.log --fork
    
    # 配置服务器 3
    mongod --configsvr --replSet cfgReplSet --dbpath /data/db/configdb03 --port 27021 --bind_ip localhost --logpath /data/log/config/configdb03.log --fork
    

    提示:对于生产环境,将 localhost 替换为实际的 IP 地址或主机名。

  2. 初始化配置副本集:连接到其中一个配置服务器实例并初始化副本集。

    mongosh --port 27019
    

    在 mongo shell 中:

    rs.initiate({
       _id: "cfgReplSet",
       configsvr: true,
       members: [
          { _id : 0, host : "localhost:27019" },
          { _id : 1, host : "localhost:27020" },
          { _id : 2, host : "localhost:27021" }
       ]
    });
    

    验证状态:

    rs.status();
    

步骤 2:设置分片副本集

集群中的每个分片都是一个副本集。我们将设置两个分片(shard01shard02),每个分片包含三个成员。

  1. 启动分片 1 成员的 mongod 实例:每个实例需要 --shardsvr--replSet 选项。

    # 分片 1 成员 1
    mongod --shardsvr --replSet shard01 --dbpath /data/db/shard01-rs01 --port 27030 --bind_ip localhost --logpath /data/log/shard01/shard01-rs01.log --fork
    
    # 分片 1 成员 2
    mongod --shardsvr --replSet shard01 --dbpath /data/db/shard01-rs02 --port 27031 --bind_ip localhost --logpath /data/log/shard01/shard01-rs02.log --fork
    
    # 分片 1 成员 3
    mongod --shardsvr --replSet shard01 --dbpath /data/db/shard01-rs03 --port 27032 --bind_ip localhost --logpath /data/log/shard01/shard01-rs03.log --fork
    
  2. 初始化分片 1 副本集:连接到其中一个分片 1 实例。

    mongosh --port 27030
    

    在 mongo shell 中:

    rs.initiate({
       _id : "shard01",
       members: [
          { _id : 0, host : "localhost:27030" },
          { _id : 1, host : "localhost:27031" },
          { _id : 2, host : "localhost:27032" }
       ]
    });
    
  3. 启动分片 2 成员的 mongod 实例(对其他分片重复):

    # 分片 2 成员 1
    mongod --shardsvr --replSet shard02 --dbpath /data/db/shard02-rs01 --port 27040 --bind_ip localhost --logpath /data/log/shard02/shard02-rs01.log --fork
    
    # 分片 2 成员 2
    mongod --shardsvr --replSet shard02 --dbpath /data/db/shard02-rs02 --port 27041 --bind_ip localhost --logpath /data/log/shard02/shard02-rs02.log --fork
    
    # 分片 2 成员 3
    mongod --shardsvr --replSet shard02 --dbpath /data/db/shard02-rs03 --port 27042 --bind_ip localhost --logpath /data/log/shard02/shard02-rs03.log --fork
    
  4. 初始化分片 2 副本集:连接到其中一个分片 2 实例。

    mongosh --port 27040
    

    在 mongo shell 中:

    rs.initiate({
       _id : "shard02",
       members: [
          { _id : 0, host : "localhost:27040" },
          { _id : 1, host : "localhost:27041" },
          { _id : 2, host : "localhost:27042" }
       ]
    });
    

步骤 3:设置 mongos 路由器

mongos 实例是客户端应用程序的入口点。它们需要知道配置服务器的位置。

  1. 启动 mongos 实例:提供 --configdb 选项,列出配置副本集成员。

    # Mongos 路由器 1
    mongos --configdb cfgReplSet/localhost:27019,localhost:27020,localhost:27021 --port 27017 --bind_ip localhost --logpath /data/log/mongos/mongos01.log --fork
    

    注意:您可以启动多个 mongos 实例以实现负载均衡和高可用性。它们都连接到相同的配置服务器。

步骤 4:连接到 mongos 并添加分片

现在,连接到 mongos 实例并将分片副本集添加到集群。

  1. 连接到 mongos:使用默认的 MongoDB 端口 27017 或您为 mongos 指定的自定义端口。

    mongosh --port 27017
    
  2. 添加分片:使用 sh.addShard() 命令,指定副本集名称及其一个成员。

    sh.addShard("shard01/localhost:27030");
    sh.addShard("shard02/localhost:27040");
    

步骤 5:为数据库和集合启用分片

添加分片后,您需要为特定数据库启用分片,然后为该数据库中的特定集合启用分片。这需要选择一个 分片键

  1. 为数据库启用分片:切换到要分片的数据库并运行 sh.enableSharding()

    use mydatabase;
    sh.enableSharding("mydatabase");
    
  2. 分片一个集合:选择一个 分片键 并使用 sh.shardCollection()

    警告:选择有效的分片键对于性能和均匀分布至关重要。糟糕的分片键可能导致热点或低效查询。常见策略包括哈希键、范围键或复合键。

    在本示例中,假设有一个包含字段 _id 的集合 mycollection

    sh.shardCollection("mydatabase.mycollection", { _id: "hashed" });
    

    对于教程来说,哈希 _id 分片键很简单,因为它比单调递增的范围 _id 键能更好地分散插入操作。对于实际应用,应根据查询模式和写入分布选择分片键,而不仅仅为了方便。

步骤 6:验证集群

在连接到 mongosmongosh 中运行以下命令:

sh.status();
db.adminCommand({ listShards: 1 });

然后插入示例文档并检查分片集合是否存在:

use mydatabase;
db.mycollection.insertMany([
  { _id: 1, name: "alpha" },
  { _id: 2, name: "beta" },
  { _id: 3, name: "gamma" }
]);

db.mycollection.getShardDistribution();

小的测试数据集可能不会立即拆分,因此不要期望在仅插入几个文档后就能完美分布。重要的第一步检查是 sh.status() 列出两个分片并显示 mydatabase.mycollection 已分片。

生产环境注意事项

此本地主机设置有助于学习各个组件,但生产环境需要更多关注:

  • 使用真实主机名,而不是 localhost,因为副本集成员名称存储在集群元数据中。
  • 将配置服务器作为三个成员的副本集运行。
  • 将每个分片作为副本集运行,成员分布在不同的故障域中。
  • 在暴露集群之前启用身份验证和内部密钥文件或 x.509 身份验证。
  • 作为集群感知备份计划的一部分,备份配置服务器元数据和分片数据。
  • 监控块分布、均衡器活动、复制延迟和磁盘增长。

最终要点

MongoDB 分片集群有三个任务:配置服务器跟踪元数据,分片副本集存储数据,mongos 路由客户端流量。首先让这些角色正常工作,然后将大部分设计时间花在分片键上,因为这一选择决定了您的集群是均匀分布负载还是产生热点分片。