MongoDB分片集

分片集基本概念

以往的副本集会受到硬件配置的限制,如果写性能出现瓶颈,只能依靠拉高CPU、内存配置来提升性能,而分片集群无需考虑这点,毕竟整个集群都是由不同节点组合对外服务,如果性能出现瓶颈,只需要继续增加分片节点即可,相较于副本集群,分片集群的上限更高。

  • Router路由:负责接收、分发客户端的读写请求,类似于代理中间件;

  • Shard分片:存储数据、处理读写请求的具体节点;

  • Config配置:存储路由、分片节点的元数据和配置信息;

这三种角色简单理解就是:一个负责请求调度、一个负责数据存储、一个负责配置管理,不过这里要注意,路由可以由一或多个mongos节点组成,config同理,而shard则并非单独的mongodb节点,而是一个个的replicaSet复制集(副本集群)。

MongoDB分片集群中的概念

  • Shard Rules:分片规则,也叫分片算法,指分发读写请求的逻辑,如随机、取模等;

  • Shard Key:分片键,也叫路由键,指基于文档中的哪个字段进行分片计算;

  • Document:文档,这个概念在前面就一直接触过,等价于MySQL中的一条数据;

  • Chunk:块,指包含一定范围内多个文档的数据段,属于集群中分割、存储数据的基本单位;

  • Shard:分片,每个分片都由一个副本集群组成,一个分片中可以存储多个Chunk数据块;

  • Cluster:集群,由多个Shard分片组成,统一对外提供读写服务。

Redis和MongoDB的区别

MongoDB分片集群对比Redis分片集群,或者传统的分库分表技术而言,还有一个无与伦比的优势,即:在集群的分片动态伸缩(数量发生变化)时,会自动触发数据迁移机制,由MongoDB内部来自动维护数据的平衡,无需任何外力介入!像“数据均衡”这个功能,无论在Redis、还是分库分表中,都需要开发者手动完成数据迁移,维护成本特别高。

分片集群搭建

基于“硬性底线”,配置需求:

至少 9 台独立服务器(或虚拟机):3台用于配置服务器副本集(Config Server),3台用于第一个分片的副本集(Shard1),另外3台用于第二个分片的副本集(Shard2);外加 2台独立部署 mongos 路由进程(可与上述9台中的任意节点复用物理机,但严禁与任何 mongod 进程共用同一服务器,故实际仍需额外2台,或使用容器/PaaS单独调度)。

若按严格物理隔离(最佳实践),则需要 11台独立机器

  • 3台(Config Server,每台最低 2核4GB,存储建议SSD 50GB+)

  • 3台(Shard1 副本集,生产推荐 8核32GB+,存储按数据量)

  • 3台(Shard2 副本集,同规格)

  • 2台(mongos,最低 4核8GB,无状态,可共置于应用侧)

如何搭建完整的副本集群,前面已经学习过了,这样最终所需的节点数量变为了:2+2+1=5,把所有涉及到副本集群的角色,全都阉割成一个节点,也就是每个副本集只有主节点,不为其添加副本节点、仲裁节点

机器规划

机器

IP

部署角色

端口

副本集名称

启动脚本

MongoDB-1

192.168.7.110

分片1(主)

27017

zhuzi-shard-1

mongod

MongoDB-2

192.168.7.111

分片2(主)

27017

zhuzi-shard-2

mongod

MongoDB-3

192.168.7.112

配置节点(主)

27017

zhuzi-config

mongod

MongoDB-4

192.168.7.113

路由1

27017

mongos

MongoDB-5

192.168.7.114

路由2

27017

mongos

设置环境变量永久生效(为了防止操作下面步骤指令无效请设置好环境变量)

 # 编辑文件
 vim /etc/profile
 ​
 # 在末尾添加
 export PATH=$PATH:/usr/local/mongodb/bin
 ​
 # 重新加载系统环境变量配置文件,立即生效
 source /etc/profile

MongoDB 内部副本集通信使用的是主机名

 # 在所有机器上执行
 cat >> /etc/hosts << 'EOF'
 192.168.7.110 MongoDB-1
 192.168.7.111 MongoDB-2
 192.168.7.112 MongoDB-3
 192.168.7.113 MongoDB-4
 192.168.7.114 MongoDB-5
 EOF

所有机器前置准备(5台都执行)

 # 1. 创建目录
 mkdir -p /usr/local/mongodb/{data,log,conf}/standalone
 ​
 # 2. 目录结构
 # /usr/local/mongodb/
 # ├── data/standalone/      数据目录
 # ├── log/standalone/       日志目录
 # └── conf/standalone/      配置文件目录
 ​
 # 安装MongoDB及mongosh

逐台机器部署

机器1(192.168.7.110)——部署分片1

 # 编辑配置文件
 vim /usr/local/mongodb/conf/standalone/mongod.conf
 ​
 # 添加以下内容
 systemLog:                          # 系统日志配置
   destination: file                 # 日志输出目标:文件
   path: "/usr/local/mongodb/log/standalone/mongo.log"   # 日志文件存放路径
   logAppend: true                   # 重启时追加写入日志,不覆盖原有日志
 ​
 storage:                            # 存储配置
   dbPath: "/usr/local/mongodb/data/standalone"   # 数据文件存放路径
 ​
 processManagement:                  # 进程管理配置
   fork: true                        # 以后台守护进程方式运行(后台运行)
   pidFilePath: "/usr/local/mongodb/conf/standalone/mongod.pid"   # PID进程ID文件存放路径
 ​
 net:                                # 网络配置
   bindIp: 0.0.0.0                   # 监听所有IP地址(允许任何机器连接)
   port: 27017                       # 服务端口号
 ​
 replication:                        # 副本集配置
   replSetName: zhuzi-shard-1        # 副本集名称
 ​
 sharding:                           # 分片集群配置
   clusterRole: shardsvr             # 节点角色:分片节点(shardsvr)
   
   
 # 启动
 mongod -f /usr/local/mongodb/conf/standalone/mongod.conf
 ​
 # 初始化副本集
 mongosh 192.168.7.110:27017 --eval "rs.initiate()"
 ​
 '生产环境存在副本节点(需要在主节点上添加)(当前环境下无需操作)'
 mongosh 192.168.7.110:27017 --eval "rs.add('192.168.7.xxx:27017')"
 mongosh 192.168.7.110:27017 --eval "rs.add('192.168.7.xxx:27017')"
 ​
 # 验证
 mongosh 192.168.7.110:27017 --eval "rs.status()"

机器2(192.168.7.111)——部署分片2

 # 编辑配置文件
 vim /usr/local/mongodb/conf/standalone/mongod.conf
 ​
 # 添加以下内容
 systemLog:                          # 系统日志配置
   destination: file                 # 日志输出目标:文件
   path: "/usr/local/mongodb/log/standalone/mongo.log"   # 日志文件存放路径
   logAppend: true                   # 重启时追加写入日志,不覆盖原有日志
 ​
 storage:                            # 存储配置
   dbPath: "/usr/local/mongodb/data/standalone"   # 数据文件存放路径
 ​
 processManagement:                  # 进程管理配置
   fork: true                        # 以后台守护进程方式运行(后台运行)
   pidFilePath: "/usr/local/mongodb/conf/standalone/mongod.pid"   # PID进程ID文件存放路径
 ​
 net:                                # 网络配置
   bindIp: 0.0.0.0                   # 监听所有IP地址(允许任何机器连接)
   port: 27017                       # 服务端口号
 ​
 replication:                        # 副本集配置
   replSetName: zhuzi-shard-2        # 副本集名称
 ​
 sharding:                           # 分片集群配置
   clusterRole: shardsvr             # 节点角色:分片节点(shardsvr)
   
   
 # 启动
 mongod -f /usr/local/mongodb/conf/standalone/mongod.conf
 ​
 # 初始化副本集
 mongosh 192.168.7.111:27017 --eval "rs.initiate()"
 ​
 '生产环境存在副本节点(需要在主节点上添加)(当前环境下无需操作)'
 mongosh 192.168.7.111:27017 --eval "rs.add('192.168.7.xxx:27017')"
 mongosh 192.168.7.111:27017 --eval "rs.add('192.168.7.xxx:27017')"
 ​
 # 验证
 mongosh 192.168.7.111:27017 --eval "rs.status()"

机器3(192.168.7.112)——部署配置节点

 # 编辑配置文件
 vim /usr/local/mongodb/conf/standalone/mongod.conf
 ​
 # 添加以下内容
 systemLog:                          # 系统日志配置
   destination: file                 # 日志输出目标:文件
   path: "/usr/local/mongodb/log/standalone/mongo.log"   # 日志文件存放路径
   logAppend: true                   # 重启时追加写入日志,不覆盖原有日志
 ​
 storage:                            # 存储配置
   dbPath: "/usr/local/mongodb/data/standalone"   # 数据文件存放路径
 ​
 processManagement:                  # 进程管理配置
   fork: true                        # 以后台守护进程方式运行(后台运行)
   pidFilePath: "/usr/local/mongodb/conf/standalone/mongod.pid"   # PID进程ID文件存放路径
 ​
 net:                                # 网络配置
   bindIp: 0.0.0.0                   # 监听所有IP地址(允许任何机器连接)
   port: 27017                       # 服务端口号
 ​
 replication:                        # 副本集配置
   replSetName: zhuzi-config         # 副本集名称
 ​
 sharding:                           # 分片集群配置
   clusterRole: configsvr            # 节点角色:配置节点(configsvr)
 ​
 ​
 # 启动
 mongod -f /usr/local/mongodb/conf/standalone/mongod.conf
 ​
 # 初始化副本集
 mongosh 192.168.7.112:27017 --eval "rs.initiate()"
 ​
 '生产环境存在副本节点(需要在主节点上添加)(当前环境下无需操作)'
 mongosh 192.168.7.112:27017 --eval "rs.add('192.168.7.xxx:27017')"
 mongosh 192.168.7.112:27017 --eval "rs.add('192.168.7.xxx:27017')"
 ​
 # 验证
 mongosh 192.168.7.112:27017 --eval "rs.status()"

机器4(192.168.7.113)——部署路由1

 # 编辑配置文件
 vim /usr/local/mongodb/conf/standalone/mongos.conf
 ​
 # 添加以下内容
 systemLog:                          # 系统日志配置
   destination: file                 # 日志输出目标:文件
   path: "/usr/local/mongodb/log/standalone/mongo.log"   # 日志文件存放路径
   logAppend: true                   # 重启时追加写入日志,不覆盖原有日志
 ​
 processManagement:                  # 进程管理配置
   fork: true                        # 以后台守护进程方式运行(后台运行)
   pidFilePath: "/usr/local/mongodb/conf/standalone/mongos.pid"   # PID进程ID文件存放路径
 ​
 net:                                # 网络配置
   bindIp: 0.0.0.0                   # 监听所有IP地址(允许任何机器连接)
   port: 27017                       # 服务端口号
 ​
 sharding:                           # 分片集群配置
   configDB: zhuzi-config/192.168.7.112:27017   # 指定配置节点地址
 ​
 '生产环境存在副本节点(需要一起添加)(当前环境下无需操作)'
 configDB: zhuzi-config/192.168.7.xxx:27017,192.168.7.xxx:27017,192.168.7.xxx:27017
 ​
 # 启动(注意:路由节点使用 mongos 命令,不是 mongod)
 mongos -f /usr/local/mongodb/conf/standalone/mongos.conf
 ​
 # 验证连接
 mongosh 192.168.7.113:27017 --eval "db.isMaster()"

机器5(192.168.7.114)——部署路由2

 # 编辑配置文件
 vim /usr/local/mongodb/conf/standalone/mongos.conf
 ​
 # 添加以下内容
 systemLog:                          # 系统日志配置
   destination: file                 # 日志输出目标:文件
   path: "/usr/local/mongodb/log/standalone/mongo.log"   # 日志文件存放路径
   logAppend: true                   # 重启时追加写入日志,不覆盖原有日志
 ​
 processManagement:                  # 进程管理配置
   fork: true                        # 以后台守护进程方式运行(后台运行)
   pidFilePath: "/usr/local/mongodb/conf/standalone/mongos.pid"   # PID进程ID文件存放路径
 ​
 net:                                # 网络配置
   bindIp: 0.0.0.0                   # 监听所有IP地址(允许任何机器连接)
   port: 27017                       # 服务端口号
 ​
 sharding:                           # 分片集群配置
   configDB: zhuzi-config/192.168.7.112:27017   # 指定配置节点地址
 ​
 '生产环境存在副本节点(需要一起添加)(当前环境下无需操作)'
 configDB: zhuzi-config/192.168.7.xxx:27017,192.168.7.xxx:27017,192.168.7.xxx:27017
 ​
 # 启动(注意:路由节点使用 mongos 命令,不是 mongod)
 mongos -f /usr/local/mongodb/conf/standalone/mongos.conf
 ​
 # 验证连接
 mongosh 192.168.7.114:27017 --eval "db.isMaster()"

添加分片到集群(在任意路由节点执行)

 # 连接路由1或路由2:
 mongosh 192.168.7.113:27017
 # 或
 mongosh 192.168.7.114:27017
 ​
 # 执行添加分片命令:
 use admin;
 ​
 // 用主机名添加分片1
 sh.addShard("zhuzi-shard-1/MongoDB-1:27017");
 ​
 '生产环境存在副本节点(需要一起添加)(当前环境下无需操作)'
 sh.addShard("zhuzi-shard-1/MongoDB-x:27017,MongoDB-x:27017,MongoDB-x:27017");
 ​
 // 用主机名添加分片2
 sh.addShard("zhuzi-shard-2/MongoDB-2:27017");
 ​
 '生产环境存在副本节点(需要一起添加)(当前环境下无需操作)'
 sh.addShard("zhuzi-shard-1/MongoDB-x:27017,MongoDB-x:27017,MongoDB-x:27017");
 ​
 // 查看分片状态
 sh.status();

验证测试

 # 连接路由1(MongoDB-4)
 mongosh 192.168.7.113:27017
 ​
 # 或者连接路由2(MongoDB-5)
 mongosh 192.168.7.114:27017
 ​
 // 切换到测试库
 use zhuzi;
 ​
 // 对数据库启用分片
 sh.enableSharding("zhuzi");
 ​
 // 对集合指定分片键(使用 hashed 分片,数据分布更均匀)
 sh.shardCollection("zhuzi.pandas", { _id: "hashed" });
 ​
 // 插入测试数据
 db.pandas.insertOne({_id: 1, name: "肥肥"});
 db.pandas.insertOne({_id: 2, name: "萌萌"});
 db.pandas.insertOne({_id: 3, name: "豆豆"});
 db.pandas.insertOne({_id: 4, name: "花花"});
 db.pandas.insertOne({_id: 5, name: "球球"});
 ​
 // 查看数据分布
 db.pandas.getShardDistribution();
 ​
 // 查看分片集群整体状态
 sh.status();

最终架构图

                     ┌─────────────────┐
                     │   路由节点       │
                     │ MongoDB-4:27017 │
                     │ MongoDB-5:27017 │
                     └────────┬────────┘
                              │
                     ┌────────▼────────┐
                     │  配置节点        │
                     │ MongoDB-3:27017 │
                     │ zhuzi-config    │
                     └─────────────────┘
                              │
         ┌────────────────────┼────────────────────┐
         │                                         │
 ┌───────▼───────┐                         ┌───────▼───────┐
 │   分片1        │                         │   分片2       │
 │ MongoDB-1     │                         │ MongoDB-2     │
 │ zhuzi-shard-1 │                         │ zhuzi-shard-2 │
 │ 业务数据 50%   │                          │ 业务数据 50%  │
 └───────────────┘                         └───────────────┘

集群信息卡片

 ╔══════════════════════════════════════════════════════════════╗
 ║              🎊 分片集群搭建成功 🎊                             ║
 ╠══════════════════════════════════════════════════════════════╣
 ║  分片1:  MongoDB-1:27017  (zhuzi-shard-1)  ✅                 ║
 ║  分片2:  MongoDB-2:27017  (zhuzi-shard-2)  ✅                 ║
 ║  配置:   MongoDB-3:27017  (zhuzi-config)   ✅                 ║
 ║  路由1:  MongoDB-4:27017                    ✅                ║
 ║  路由2:  MongoDB-5:27017                    ✅                ║
 ╠══════════════════════════════════════════════════════════════╣
 ║  已分片数据库: zhuzi                                           ║
 ║  已分片集合:   zhuzi.pandas                                    ║
 ║  分片键:       { _id: "hashed" }                              ║
 ║  数据块数:     4 个                                            ║
 ║  当前数据:     5 条文档                                        ║
 ╚══════════════════════════════════════════════════════════════╝

故障排查清单

问题

排查命令

路由连接不上

ps -ef | grep mongos

分片节点挂了

ps -ef | grep mongod

数据分布不均

sh.isBalancerRunning()

查看日志

tail -100 /usr/local/mongodb/log/standalone/mongo.log