MongoDB分片集群部署与高可用测试实战指南

在MongoDB的使用过程中,当数据量增长到单机或普通副本集无法承载时,分片集群(Sharded Cluster)就成为解决大规模数据存储与高并发写入的核心方案。本文将从分片集群的适用场景、架构原理出发,一步步带你完成从环境部署到高可用测试的全流程实战,帮助你快速掌握MongoDB分片集群的搭建与验证方法。

一、什么时候该用MongoDB分片集群?

并非所有场景都需要分片集群------普通副本集(Replica Set)已能满足大部分中小规模业务的高可用需求。只有当出现以下情况时,才需要考虑引入分片:

  1. 数据量过大,单机性能瓶颈凸显

    当单机MongoDB的磁盘占用过高(如超过TB级),或单机备份/恢复耗时过长(如超过数小时),会导致运维效率下降、故障恢复风险升高,此时需要通过分片将数据分散到多台机器。

  2. 数据持续增长,未来存在扩容需求

    如果业务预估数据量或请求量会持续增长(如用户量、日志量按月翻倍),提前规划分片集群可避免后续因单机瓶颈导致的业务中断,实现"按需扩容"。

  3. 副本集无法满足高并发写入需求

    副本集的写入操作仅在主节点(Primary)执行,再同步到从节点(Secondary),当写入QPS超过主节点性能上限(如每秒数万次写入)时,即使增加从节点也无法提升写入能力,而分片可将写入压力分散到多个分片的主节点。

注意:分片集群的组件更多(路由、配置、分片节点),维护成本高于副本集。若副本集能满足业务需求,优先选择副本集。

二、MongoDB分片集群的核心特点

分片集群之所以成为大规模MongoDB部署的首选,源于其三大核心优势:

  1. 对应用透明,低改造成本

    应用只需连接mongos(路由节点),无需感知后端分片结构,操作语法与单机/MongoDB完全一致(如insertfind),无需大量修改业务代码。

  2. 数据自动平衡,避免"冷热分片"

    集群会实时监测各分片的数据量,当某个分片数据量远超其他分片时,自动将部分数据迁移到空闲分片,确保所有分片负载均衡,无需人工干预。

  3. 动态扩容,不中断业务

    扩容时只需新增分片节点,加入集群后数据会自动迁移,整个过程中MongoDB可正常提供服务,实现"在线扩容",满足生产环境的高可用要求。

三、分片集群架构解析

MongoDB分片集群由三大核心组件构成,各组件各司其职,且均通过副本集保证高可用:

组件 作用 高可用设计
mongos 路由节点,接收应用请求并转发到对应分片 多台部署(如每台应用机1个)
Config Servers 配置服务器,存储集群元数据(分片映射) 固定3节点副本集(无Arbiter)
Shard 分片节点,实际存储数据 每个Shard为1个副本集(3节点)

本次实验架构规划

为了降低实验成本,我们使用3台物理机搭建完整分片集群,具体规划如下:

  • Config Servers :3台机器均部署,端口27020,组成副本集config_repl
  • Shard节点 :每台机器部署2个分片实例(端口2702127022),其中:
    • 3台机器的27021实例组成分片1(副本集shardsvr_repl_27021);
    • 3台机器的27022实例组成分片2(副本集shardsvr_repl_27022);
  • mongos :任选1台机器部署,端口27017(生产环境建议多机部署)。

四、实战部署:从0搭建MongoDB分片集群

前置准备

  • 3台Linux机器(本文使用CentOS 7,IP分别为192.168.184.151192.168.184.152192.168.184.153);
  • 每台机器已安装MongoDB 5.0+(安装步骤参考MongoDB官方文档);
  • 关闭防火墙或开放所需端口(27017270202702127022)。

4.1 部署Config Servers(配置服务器)

Config Servers是集群的"大脑",需先部署且必须为3节点副本集(不支持Arbiter)。

步骤1:在3台机器上创建目录
bash 复制代码
# 创建Config Server的数据、配置、日志、进程文件目录
mkdir -p /data/mongodbconfig27020/{data,conf,run,logs}
步骤2:编写Config Server配置文件

在每台机器上编辑/data/mongodbconfig27020/conf/mongod.conf,内容如下:

yaml 复制代码
sharding:
  clusterRole: configsvr  # 标识为Config Server
systemLog:
  destination: file
  logAppend: true
  path: /data/mongodbconfig27020/logs/mongod.log  # 日志路径
storage:
  dbPath: /data/mongodbconfig27020/data  # 数据存储路径
  journal:
    enabled: true  # 开启日志 journal,防止数据丢失
processManagement:
  fork: true  # 后台运行
  pidFilePath: /data/mongodbconfig27020/run/mongod.pid  # PID文件路径
  timeZoneInfo: /usr/share/zoneinfo  # 时区信息
net:
  port: 27020  # 端口
  bindIp: 0.0.0.0  # 允许所有IP访问(生产环境需限制)
replication:
  oplogSizeMB: 4096  #  oplog大小(4GB,根据数据量调整)
  replSetName: config_repl  # 副本集名称,3台机器需一致
步骤3:启动Config Server

在3台机器上分别执行启动命令:

bash 复制代码
mongod -f /data/mongodbconfig27020/conf/mongod.conf
步骤4:初始化Config Server副本集

仅在其中1台机器(如192.168.184.151)上执行初始化:

bash 复制代码
# 连接Config Server
mongosh --port 27020

# 初始化副本集(执行后会自动选举主节点)
rs.initiate({
  _id: "config_repl",
  configsvr: true,  # 必须指定为Config Server副本集
  members: [
    {_id: 0, host: '192.168.184.151:27020'},
    {_id: 1, host: '192.168.184.152:27020'},
    {_id: 2, host: '192.168.184.153:27020'}
  ]
})

# 查看副本集状态(确认所有节点正常)
rs.status()

注意 :Config Server副本集不能添加Arbiter节点,必须由3个数据节点组成。

4.2 部署Shard节点(分片实例)

每个Shard是一个独立的副本集,本文部署2个Shard(分别使用端口2702127022)。

步骤1:在3台机器上创建Shard目录
bash 复制代码
# 为2个Shard分别创建目录(27021和27022)
mkdir -p /data/mongodbshard27021/{data,conf,run,logs}
mkdir -p /data/mongodbshard27022/{data,conf,run,logs}
步骤2:编写Shard配置文件
(1)Shard 1(端口27021)的配置文件

编辑/data/mongodbshard27021/conf/mongod.conf

yaml 复制代码
sharding:
  clusterRole: shardsvr  # 标识为Shard节点
systemLog:
  destination: file
  logAppend: true
  path: /data/mongodbshard27021/logs/mongod.log
storage:
  dbPath: /data/mongodbshard27021/data
  journal:
    enabled: true
processManagement:
  fork: true
  pidFilePath: /data/mongodbshard27021/run/mongod.pid
  timeZoneInfo: /usr/share/zoneinfo
net:
  port: 27021
  bindIp: 0.0.0.0
replication:
  oplogSizeMB: 4096
  replSetName: shardsvr_repl_27021  # Shard 1的副本集名称
(2)Shard 2(端口27022)的配置文件

编辑/data/mongodbshard27022/conf/mongod.conf

yaml 复制代码
sharding:
  clusterRole: shardsvr
systemLog:
  destination: file
  logAppend: true
  path: /data/mongodbshard27022/logs/mongod.log
storage:
  dbPath: /data/mongodbshard27022/data
  journal:
    enabled: true
processManagement:
  fork: true
  pidFilePath: /data/mongodbshard27022/run/mongod.pid
  timeZoneInfo: /usr/share/zoneinfo
net:
  port: 27022
  bindIp: 0.0.0.0
replication:
  oplogSizeMB: 4096
  replSetName: shardsvr_repl_27022  # Shard 2的副本集名称
步骤3:启动Shard节点

在3台机器上分别启动2个Shard实例:

bash 复制代码
# 启动Shard 1(27021)
mongod -f /data/mongodbshard27021/conf/mongod.conf

# 启动Shard 2(27022)
mongod -f /data/mongodbshard27022/conf/mongod.conf
步骤4:初始化Shard副本集

分别初始化Shard 1和Shard 2的副本集(仅在其中1台机器执行)。

(1)初始化Shard 1(27021)
bash 复制代码
# 连接Shard 1
mongosh --port 27021

# 初始化副本集
rs.initiate({
  _id: "shardsvr_repl_27021",
  members: [
    {_id: 0, host: '192.168.184.151:27021'},
    {_id: 1, host: '192.168.184.152:27021'},
    {_id: 2, host: '192.168.184.153:27021'}
  ]
})

# 查看状态
rs.status()
(2)初始化Shard 2(27022)
bash 复制代码
# 连接Shard 2
mongosh --port 27022

# 初始化副本集
rs.initiate({
  _id: "shardsvr_repl_27022",
  members: [
    {_id: 0, host: '192.168.184.151:27022'},
    {_id: 1, host: '192.168.184.152:27022'},
    {_id: 2, host: '192.168.184.153:27022'}
  ]
})

# 查看状态
rs.status()

4.3 部署mongos(路由节点)

mongos是应用的接入点,需指定Config Server地址以获取集群元数据。

步骤1:创建mongos目录

在部署mongos的机器(如192.168.184.151)上执行:

bash 复制代码
mkdir -p /data/mongos27017/{logs,conf,run}
步骤2:编写mongos配置文件

编辑/data/mongos27017/conf/mongos.conf

yaml 复制代码
sharding:
  configDB: config_repl/192.168.184.151:27020,192.168.184.152:27020,192.168.184.153:27020  # 关联Config Server副本集
systemLog:
  destination: file
  logAppend: true
  path: /data/mongos27017/logs/mongos.log
processManagement:
  fork: true
  pidFilePath: /data/mongos27017/run/mongod.pid
net:
  port: 27017  # 默认MongoDB端口,方便应用连接
  bindIp: 0.0.0.0
步骤3:启动mongos
bash 复制代码
mongos --config /data/mongos27017/conf/mongos.conf
步骤4:创建集群用户

mongos本身不存储数据,用户信息会同步到Config Server。

(1)创建管理员用户(root)
bash 复制代码
# 连接mongos
mongosh --port 27017

# 切换到admin库
use admin

# 创建管理员用户(拥有所有库的用户管理权限)
db.createUser({
  user: "root",
  pwd: passwordPrompt(),  # 输入密码(本文示例:U8agi79Qa)
  roles: [{role: "userAdminAnyDatabase", db: "admin"}]
})

# 退出后重新登录(验证权限)
mongosh --port 27017 -u root -p
(2)创建业务用户(martin_rw)

为业务库martin创建读写用户:

bash 复制代码
# 切换到martin库(不存在会自动创建)
use martin

# 创建业务用户(仅拥有martin库的读写权限)
db.createUser({
  user: "martin_rw",
  pwd: passwordPrompt(),  # 输入密码(本文示例:Augacad8G)
  roles: [{role: "readWrite", db: "martin"}]
})

4.4 配置分片集群与数据测试

步骤1:添加Shard到集群

通过管理员用户登录mongos,将2个Shard加入集群:

bash 复制代码
# 连接mongos(管理员权限)
mongosh --port 27017 -u root -p

# 添加Shard 1
sh.addShard("shardsvr_repl_27021/192.168.184.151:27021,192.168.184.152:27021,192.168.184.153:27021")

# 添加Shard 2
sh.addShard("shardsvr_repl_27022/192.168.184.151:27022,192.168.184.152:27022,192.168.184.153:27022")

# 查看集群状态(确认Shard已添加)
sh.status()


步骤2:启用数据库分片

martin库启用分片功能:

bash 复制代码
# 启用martin库的分片
sh.enableSharding("martin")
步骤3:配置集合分片规则

选择userinfo集合,按_id字段的哈希值分片(哈希分片适合数据均匀分布场景):

bash 复制代码
# 对martin.userinfo集合配置分片规则
sh.shardCollection("martin.userinfo", {_id: "hashed"})

# 查看分片详情
sh.status()


步骤4:验证数据分布

通过业务用户插入测试数据,验证数据是否分散到2个Shard:

bash 复制代码
# 用业务用户登录mongos
mongosh --port 27017 -u martin_rw -p --authenticationDatabase martin

# 切换到martin库
use martin

# 插入20条测试数据
for (var i=1; i<=20; i++) db.userinfo.insertOne({userid:i, username:'test_user_' + i});

# 查看总数据量(应返回20)
db.userinfo.countDocuments()

分别登录2个Shard查看数据分布:

bash 复制代码
# 登录Shard 1(27021),查看数据量(约10条)
mongosh --port 27021 martin
db.getMongo().setReadPref("secondaryPreferred")
db.userinfo.countDocuments()

# 登录Shard 2(27022),查看数据量(约10条)
mongosh --port 27022 martin
db.getMongo().setReadPref("secondaryPreferred")
db.userinfo.countDocuments()

结果会显示数据均匀分布在2个Shard,验证分片规则生效。

五、高可用测试:模拟节点故障

分片集群的高可用依赖于副本集的自动故障转移。本节通过"kill Shard主节点"模拟故障,验证集群是否能正常提供服务。

5.1 编写Go语言数据写入程序

首先编写一个Go程序,每秒向集群写入1条数据,并打印日志(模拟业务持续写入):

go 复制代码
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
	// 连接mongos(指定业务用户、密码、认证库)
	uri := "mongodb://martin_rw:Augacad8G@192.168.184.151:27017/?authSource=martin"
	clientOpts := options.Client().ApplyURI(uri)
	client, err := mongo.Connect(context.TODO(), clientOpts)
	if err != nil {
		log.Fatalf("连接MongoDB失败:%v", err)
	}

	// 验证连接
	if err := client.Ping(context.TODO(), nil); err != nil {
		log.Fatalf("Ping MongoDB失败:%v", err)
	}
	fmt.Println("已连接到MongoDB分片集群,开始每秒写入数据...")

	// 获取集合(shard_write_test)
	col := client.Database("martin").Collection("shard_write_test")

	// 每秒写入1条数据
	for i := 1; ; i++ {
		// 构造数据(包含时间戳和自增ID)
		data := bson.M{
			"write_id": i,
			"content":  "shard_ha_test",
			"write_at": time.Now(),
		}

		// 插入数据
		_, err := col.InsertOne(context.TODO(), data)
		if err != nil {
			log.Printf("[%s] 写入失败:%v", time.Now().Format("2006-01-02 15:04:05"), err)
		} else {
			log.Printf("[%s] 写入成功:write_id=%d", time.Now().Format("2006-01-02 15:04:05"), i)
		}

		// 休眠1秒
		time.Sleep(time.Second)
	}
}

5.2 运行程序并模拟故障

步骤1:运行Go程序
bash 复制代码
# 编译并运行(需提前安装Go环境和MongoDB驱动:go get go.mongodb.org/mongo-driver/mongo)
go run mongo_shard_write.go

程序会持续打印写入日志,此时集群正常运行。

步骤2:找到Shard 1的主节点
bash 复制代码
# 连接Shard 1(27021)
mongosh --port 27021 -u martin_rw -p --authenticationDatabase martin

# 查看当前主节点(输出中"primary"对应的IP即为当前主节点)
rs.status()

假设当前Shard 1的主节点是192.168.184.151:27021

步骤3:Kill主节点进程

192.168.184.151上找到Shard 1的进程并kill:

bash 复制代码
# 查找Shard 1(27021)的进程ID
ps -ef | grep mongodbshard27021

# Kill进程(替换为实际PID)
kill -9 12345
步骤4:观察程序写入情况

查看Go程序的日志,会发现:

  • 仅出现1秒左右的写入失败(副本集选举新主节点的耗时);
  • 选举完成后,程序恢复正常写入,无持续故障。
步骤5:验证数据完整性

故障恢复后,登录mongos查看数据总量,确认无数据丢失:

bash 复制代码
mongosh --port 27017 -u martin_rw -p --authenticationDatabase martin
use martin
db.shard_write_test.countDocuments()

数据量应与程序写入的总次数一致,验证集群高可用生效。

六、总结与注意事项

  1. 架构核心要点

    • Config Server必须是3节点副本集,无Arbiter;
    • 每个Shard必须是副本集(至少3节点),确保单个Shard的高可用;
    • mongos建议多机部署(如每台应用机1个),避免路由节点单点故障。
  2. 分片键选择

    • 哈希分片(如本文的_id: hashed)适合数据均匀分布,无明显访问热点;
    • 范围分片(如createTime: 1)适合按范围查询(如查询某时间段数据),但需避免热点分片。
  3. 生产环境建议

    • 关闭bindIp: 0.0.0.0,限制仅应用机器可访问;
    • 使用更复杂的密码,并定期更换;
    • 监控各组件状态(如mongos路由效率、Shard数据均衡情况),可使用MongoDB Compass或Prometheus+Grafana。

通过本文的实战,你已掌握MongoDB分片集群的部署、配置与高可用验证方法。在实际业务中,需根据数据量、访问模式调整集群规模与分片策略,确保集群稳定高效运行。

相关推荐
Jtti1 小时前
网站服务器首页正常但内页全部404是什么原因?
运维·服务器·数据库
数据库学啊1 小时前
性价比高的国产时序数据库哪家技术强
数据库·时序数据库
咖丨喱1 小时前
【修复miracast协商失败问题】
服务器·数据库·asp.net
蟹至之1 小时前
【MySQL】索引 (上) —— 索引的定义与数据结构
数据库·mysql·索引
·云扬·1 小时前
基于YCSB的MongoDB性能压测实践指南
数据库·mongodb
卿雪1 小时前
MySQL【索引】:索引的概念与分类
java·数据库·python·mysql·adb·golang
pandarking2 小时前
[CTF]攻防世界:very_easy_sql(gopher)
数据库·sql·web安全·ctf
爬山算法9 小时前
Redis(162)如何使用Redis实现消息队列?
数据库·redis·缓存
u***32439 小时前
【Redis】centos7 systemctl 启动 Redis 失败
数据库·redis·缓存