MongoDB Replica Set 中文教程
1. 核心概念
MongoDB Replica Set 是 MongoDB 官方提供的高可用性解决方案,由一组维护相同数据集的 MongoDB 实例组成。它通过主从复制和自动故障转移机制,确保 MongoDB 服务的持续可用性。
1.1 主要功能
- 数据冗余:将数据复制到多个节点,提供数据冗余和高可用性
- 自动故障转移:当主节点发生故障时,自动将一个从节点提升为新的主节点
- 读写分离:可以将读操作分发到从节点,提高系统性能
- 地理分布式部署:支持跨数据中心部署,提供灾备能力
- 选举机制:基于 Raft 协议的选举机制,确保集群一致性
1.2 架构组成
- 主节点(Primary):负责处理所有写操作和读操作(默认情况下)
- 从节点(Secondary):复制主节点的数据,可用于处理读操作
- 仲裁节点(Arbiter):不存储数据,仅参与选举过程,用于打破平局
1.3 选举机制
MongoDB Replica Set 使用基于 Raft 协议的选举机制:
- 当主节点不可用时,从节点会触发选举
- 每个节点会投票给一个候选节点
- 获得大多数投票的节点成为新的主节点
- 选举过程通常在 10 秒内完成
1.4 数据复制
主节点将操作记录写入 oplog(操作日志),从节点通过复制 oplog 来同步数据:
- 主节点执行写操作并记录到 oplog
- 从节点定期从主节点获取 oplog
- 从节点重放 oplog 中的操作,保持数据与主节点一致
- 从节点维护一个 oplog 时间戳,跟踪同步进度
2. 安装与配置
2.1 安装 MongoDB
2.1.1 在 Ubuntu/Debian 系统上安装
# 导入 MongoDB GPG 密钥
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
# 添加 MongoDB 仓库
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
# 更新包列表
sudo apt update
# 安装 MongoDB
sudo apt install -y mongodb-org
# 启动 MongoDB 服务
sudo systemctl start mongod
# 设置 MongoDB 服务开机自启
sudo systemctl enable mongod2.1.2 在 CentOS/RHEL 系统上安装
# 创建 MongoDB 仓库文件
sudo tee /etc/yum.repos.d/mongodb-org-7.0.repo << 'EOF'
[mongodb-org-7.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/7.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-7.0.asc
EOF
# 安装 MongoDB
sudo yum install -y mongodb-org
# 启动 MongoDB 服务
sudo systemctl start mongod
# 设置 MongoDB 服务开机自启
sudo systemctl enable mongod2.1.3 从源码编译安装
# 克隆 MongoDB 源码
git clone https://github.com/mongodb/mongo.git
cd mongo
# 切换到稳定版本
git checkout r7.0.0
# 安装依赖
sudo apt install -y build-essential libssl-dev libcurl4-openssl-dev liblzma-dev
# 编译 MongoDB
scons \
--prefix=/usr/local/mongodb \
--disable-warnings-as-errors \
--ssl
# 安装 MongoDB
sudo scons install --prefix=/usr/local/mongodb
# 添加 MongoDB 到 PATH
echo 'export PATH=/usr/local/mongodb/bin:$PATH' >> ~/.bashrc
source ~/.bashrc2.2 配置 MongoDB Replica Set
2.2.1 准备配置文件
创建 3 个 MongoDB 实例的配置文件(1 主 2 从):
mongod-27017.conf:
# 系统日志配置
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod-27017.log
# 存储配置
storage:
dbPath: /var/lib/mongodb/27017
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 1
# 进程控制
processManagement:
fork: true
pidFilePath: /var/run/mongodb/mongod-27017.pid
# 网络配置
net:
port: 27017
bindIp: 0.0.0.0
# 复制集配置
replication:
replSetName: rs0
# 安全配置
security:
authorization: enabled
keyFile: /etc/mongodb/keyfilemongod-27018.conf:
# 系统日志配置
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod-27018.log
# 存储配置
storage:
dbPath: /var/lib/mongodb/27018
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 1
# 进程控制
processManagement:
fork: true
pidFilePath: /var/run/mongodb/mongod-27018.pid
# 网络配置
net:
port: 27018
bindIp: 0.0.0.0
# 复制集配置
replication:
replSetName: rs0
# 安全配置
security:
authorization: enabled
keyFile: /etc/mongodb/keyfilemongod-27019.conf:
# 系统日志配置
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod-27019.log
# 存储配置
storage:
dbPath: /var/lib/mongodb/27019
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 1
# 进程控制
processManagement:
fork: true
pidFilePath: /var/run/mongodb/mongod-27019.pid
# 网络配置
net:
port: 27019
bindIp: 0.0.0.0
# 复制集配置
replication:
replSetName: rs0
# 安全配置
security:
authorization: enabled
keyFile: /etc/mongodb/keyfile2.2.2 创建数据目录和日志目录
# 创建数据目录
sudo mkdir -p /var/lib/mongodb/27017 /var/lib/mongodb/27018 /var/lib/mongodb/27019
# 创建日志目录
sudo mkdir -p /var/log/mongodb
# 创建运行目录
sudo mkdir -p /var/run/mongodb
# 设置权限
sudo chown -R mongodb:mongodb /var/lib/mongodb /var/log/mongodb /var/run/mongodb2.2.3 创建 keyFile
keyFile 用于在复制集成员之间进行身份验证:
# 创建 keyFile 目录
sudo mkdir -p /etc/mongodb
# 生成 keyFile
sudo openssl rand -base64 756 > /etc/mongodb/keyfile
# 设置权限
sudo chmod 400 /etc/mongodb/keyfile
# 设置所有者
sudo chown mongodb:mongodb /etc/mongodb/keyfile2.2.4 启动 MongoDB 实例
# 启动第一个实例
sudo -u mongodb mongod --config /etc/mongod-27017.conf
# 启动第二个实例
sudo -u mongodb mongod --config /etc/mongod-27018.conf
# 启动第三个实例
sudo -u mongodb mongod --config /etc/mongod-27019.conf2.2.5 初始化复制集
# 连接到第一个实例
mongo --port 27017
# 初始化复制集
rs.initiate({
_id: "rs0",
members: [
{
_id: 0,
host: "localhost:27017"
},
{
_id: 1,
host: "localhost:27018"
},
{
_id: 2,
host: "localhost:27019"
}
]
})
# 查看复制集状态
rs.status()2.2.6 创建管理员用户
# 连接到主节点
mongo --port 27017
# 切换到 admin 数据库
use admin
# 创建管理员用户
db.createUser({
user: "admin",
pwd: "your_strong_password",
roles: [
{ role: "root", db: "admin" }
]
})
# 验证用户创建成功
db.auth("admin", "your_strong_password")3. 基本使用
3.1 连接到复制集
3.1.1 使用 mongo 命令行工具
# 连接到复制集(会自动连接到主节点)
mongo "mongodb://admin:your_strong_password@localhost:27017,localhost:27018,localhost:27019/admin?replicaSet=rs0"
# 连接到特定节点
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin3.1.2 使用 Node.js 客户端
const { MongoClient } = require('mongodb');
// 复制集连接字符串
const uri = 'mongodb://admin:your_strong_password@localhost:27017,localhost:27018,localhost:27019/admin?replicaSet=rs0';
// 创建客户端
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
});
// 连接到 MongoDB
async function connect() {
try {
await client.connect();
console.log('Connected to MongoDB Replica Set');
// 获取数据库
const db = client.db('test');
// 执行操作
// ...
} catch (err) {
console.error('Error connecting to MongoDB:', err);
} finally {
// 关闭连接
await client.close();
}
}
connect();3.2 查看复制集状态
# 连接到主节点
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin
# 查看复制集状态
rs.status()
# 查看复制集配置
rs.conf()
# 查看从节点复制状态
rs.printSlaveReplicationInfo()
# 查看主节点状态
rs.printReplicationInfo()3.3 数据操作
3.3.1 写操作
写操作会自动路由到主节点:
# 连接到复制集
mongo "mongodb://admin:your_strong_password@localhost:27017,localhost:27018,localhost:27019/admin?replicaSet=rs0"
# 切换到 test 数据库
use test
# 插入文档
db.users.insertOne({ name: "张三", age: 25, email: "zhangsan@example.com" })
# 更新文档
db.users.updateOne({ name: "张三" }, { $set: { age: 26 } })
# 删除文档
db.users.deleteOne({ name: "张三" })3.3.2 读操作
默认情况下,读操作也会路由到主节点。可以通过设置 readPreference 来将读操作路由到从节点:
# 连接到复制集
mongo "mongodb://admin:your_strong_password@localhost:27017,localhost:27018,localhost:27019/admin?replicaSet=rs0"
# 切换到 test 数据库
use test
# 设置读偏好为从节点
db.getMongo().setReadPref("secondary")
# 执行读操作
db.users.find()
# 或者在连接字符串中指定读偏好
# mongo "mongodb://admin:your_strong_password@localhost:27017,localhost:27018,localhost:27019/admin?replicaSet=rs0&readPreference=secondary"3.4 添加和移除节点
3.4.1 添加节点
# 连接到主节点
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin
# 添加新节点
rs.add("localhost:27020")
# 添加仲裁节点
rs.addArb("localhost:27021")3.4.2 移除节点
# 连接到主节点
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin
# 移除节点
rs.remove("localhost:27020")3.5 手动故障转移
# 连接到主节点
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin
# 执行手动故障转移
rs.stepDown()4. 高级功能
4.1 读写分离
通过设置 readPreference,可以实现读写分离,将读操作分发到从节点:
4.1.1 在连接字符串中设置
const uri = 'mongodb://admin:your_strong_password@localhost:27017,localhost:27018,localhost:27019/admin?replicaSet=rs0&readPreference=secondary';4.1.2 在代码中设置
const client = new MongoClient(uri);
await client.connect();
// 获取数据库
const db = client.db('test');
// 设置读偏好
db.collection('users').find().readPreference('secondary').toArray();4.2 延迟复制
延迟复制可以用于灾难恢复,当主节点发生错误时,可以从延迟的从节点恢复数据:
# 连接到主节点
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin
# 配置延迟复制(延迟 1 小时)
cfg = rs.conf()
cfg.members[2].priority = 0 # 降低优先级,避免成为主节点
cfg.members[2].slaveDelay = 3600 # 延迟 3600 秒
rs.reconfig(cfg)4.3 隐藏节点
隐藏节点不会接收客户端请求,可用于备份或报表:
# 连接到主节点
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin
# 配置隐藏节点
cfg = rs.conf()
cfg.members[2].priority = 0 # 降低优先级
cfg.members[2].hidden = true # 设置为隐藏节点
rs.reconfig(cfg)4.4 标签感知路由
通过设置标签,可以将读操作路由到特定的节点:
# 连接到主节点
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin
# 配置节点标签
cfg = rs.conf()
cfg.members[0].tags = { "dc": "us-east", "rack": "A" }
cfg.members[1].tags = { "dc": "us-east", "rack": "B" }
cfg.members[2].tags = { "dc": "us-west", "rack": "A" }
rs.reconfig(cfg)
# 设置读偏好标签
const client = new MongoClient(uri, {
readPreference: 'secondary',
readPreferenceTags: [{ "dc": "us-east" }]
});4.5 复制集监控
4.5.1 使用 MongoDB Atlas
MongoDB Atlas 提供了全面的复制集监控功能,包括:
- 节点状态监控
- 复制延迟监控
- 操作日志监控
- 性能指标监控
4.5.2 使用命令行工具
# 查看复制集状态
rs.status()
# 查看复制延迟
rs.printSlaveReplicationInfo()
# 查看操作日志状态
db.adminCommand({ replSetGetStatus: 1 })
# 查看连接状态
db.serverStatus().connections4.6 备份与恢复
4.6.1 使用 mongodump 和 mongorestore
# 从主节点备份
timestamp=$(date +%Y%m%d_%H%M%S)
mongodump --host "rs0/localhost:27017" --username admin --password your_strong_password --out "backup_$timestamp"
# 恢复数据
mongorestore --host "rs0/localhost:27017" --username admin --password your_strong_password "backup_$timestamp"4.6.2 使用文件系统快照
如果使用支持快照的文件系统(如 LVM、ZFS),可以使用文件系统快照进行备份:
锁定数据库:
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin use admin db.fsyncLock()创建文件系统快照:
sudo lvcreate --snapshot --name mongodb_snap /dev/vg0/mongodb解锁数据库:
mongo --port 27017 -u admin -p your_strong_password --authenticationDatabase admin use admin db.fsyncUnlock()挂载快照并复制数据:
sudo mkdir -p /mnt/mongodb_snap sudo mount /dev/vg0/mongodb_snap /mnt/mongodb_snap sudo cp -r /mnt/mongodb_snap/* /backup/mongodb/ sudo umount /mnt/mongodb_snap sudo lvremove --force /dev/vg0/mongodb_snap
5. 最佳实践
5.1 部署建议
- 节点数量:建议至少部署 3 个节点(1 主 2 从),或 2 个节点加 1 个仲裁节点
- 硬件配置:根据数据量和访问量选择合适的硬件配置,主节点和从节点配置应相同
- 网络配置:确保节点之间网络畅通,网络延迟应尽可能低
- 防火墙:确保开放必要的端口(默认 27017)
- 部署位置:将节点部署在不同的物理机器或虚拟机上,避免单点故障
- 地理分布:对于关键应用,考虑跨数据中心部署,提高灾备能力
5.2 性能优化
- 内存配置:为 MongoDB 分配足够的内存,建议将 wiredTiger.cacheSizeGB 设置为系统内存的 50%
- 存储配置:使用 SSD 存储提高性能,特别是对于写密集型应用
- 索引优化:为常用查询创建合适的索引,避免全表扫描
- 批量操作:使用批量操作减少网络往返时间
- 连接池:使用连接池管理客户端连接,避免频繁创建和销毁连接
- 读写分离:将读操作分发到从节点,提高系统整体性能
5.3 安全配置
- 启用认证:启用 MongoDB 的认证机制,设置强密码
- 使用 keyFile:在复制集成员之间使用 keyFile 进行身份验证
- 限制访问:通过 bindIp 参数限制只接受特定 IP 的连接
- 使用 TLS:在生产环境中启用 TLS 加密
- 定期更新:及时更新 MongoDB 版本,修复安全漏洞
- 权限管理:根据最小权限原则,为不同用户分配适当的角色
5.4 监控与维护
- 监控指标:监控节点状态、复制延迟、内存使用、CPU 使用率、网络流量等指标
- 日志分析:定期分析 MongoDB 日志,及时发现问题
- 定期备份:制定合理的备份策略,确保数据安全
- 容量规划:根据数据增长趋势,提前规划存储容量
- 健康检查:定期执行 db.serverStatus() 和 db.stats() 检查数据库健康状态
- 索引维护:定期重建索引,提高查询性能
6. 故障排查
6.1 常见问题
- 主节点不可用:检查主节点状态,查看日志文件,确认是否有硬件或网络问题
- 复制延迟过高:检查网络连接,确认从节点硬件配置是否足够,查看是否有大量写操作
- 选举失败:检查节点数量是否足够,确认网络连接是否正常,查看日志文件中的选举相关信息
- 数据不一致:检查复制状态,确认 oplog 是否正常,可能需要重新同步从节点
- 连接问题:检查网络连接,确认 MongoDB 服务是否运行,检查防火墙设置
- 性能问题:检查索引使用情况,确认硬件配置是否足够,查看是否有慢查询
6.2 排查工具
- mongo 命令行工具:用于执行管理命令和查看状态
- MongoDB Compass:图形化管理工具,提供可视化监控
- MongoDB Atlas:云服务,提供全面的监控和管理功能
- mongostat:实时监控 MongoDB 实例的性能指标
- mongotop:监控集合级别的读写操作
- 日志文件:包含详细的操作和错误信息
6.3 示例排查过程
问题:复制集出现复制延迟
排查步骤:
查看复制状态:
rs.printSlaveReplicationInfo()查看从节点日志:
tail -f /var/log/mongodb/mongod-27018.log检查网络连接:
ping localhost telnet localhost 27017检查主节点写操作:
mongotop --host localhost:27017 --username admin --password your_strong_password检查从节点资源使用:
top -p $(pgrep -f mongod-27018) iostat -x解决方法:
- 增加从节点硬件资源
- 优化主节点写操作
- 检查网络连接,减少网络延迟
- 考虑使用分片集群分散写操作
7. 实用案例
7.1 构建高可用 MongoDB 集群
场景:构建一个高可用的 MongoDB 集群,用于存储用户数据和应用状态。
解决方案:
部署架构:
- 3 个 MongoDB 节点(1 主 2 从)
- 节点分布在 3 台不同的物理机器上
- 每台机器配置相同的硬件资源
配置步骤:
- 在每台机器上安装 MongoDB
- 配置 MongoDB 实例
- 初始化复制集
- 创建管理员用户
- 配置客户端连接
客户端配置:
const { MongoClient } = require('mongodb'); // 复制集连接字符串 const uri = 'mongodb://admin:your_strong_password@server1:27017,server2:27017,server3:27017/admin?replicaSet=rs0'; // 创建客户端 const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, maxPoolSize: 100 }); // 连接到 MongoDB async function connect() { try { await client.connect(); console.log('Connected to MongoDB Replica Set'); } catch (err) { console.error('Error connecting to MongoDB:', err); } } // 存储用户数据 async function saveUser(user) { try { const db = client.db('app'); const result = await db.collection('users').insertOne(user); return result.insertedId; } catch (err) { console.error('Error saving user:', err); throw err; } } // 获取用户数据 async function getUser(userId) { try { const db = client.db('app'); return await db.collection('users').findOne({ _id: userId }); } catch (err) { console.error('Error getting user:', err); throw err; } } // 更新用户数据 async function updateUser(userId, updates) { try { const db = client.db('app'); await db.collection('users').updateOne({ _id: userId }, { $set: updates }); } catch (err) { console.error('Error updating user:', err); throw err; } } // 导出函数 module.exports = { connect, saveUser, getUser, updateUser };使用示例:
const mongoService = require('./mongo-service'); // 启动时连接 MongoDB mongoService.connect(); // 存储用户数据 const userId = await mongoService.saveUser({ name: '张三', age: 25, email: 'zhangsan@example.com', createdAt: new Date() }); console.log('User created with ID:', userId); // 获取用户数据 const user = await mongoService.getUser(userId); console.log('User data:', user); // 更新用户数据 await mongoService.updateUser(userId, { age: 26, lastUpdated: new Date() }); console.log('User updated successfully');
7.2 使用 MongoDB Replica Set 作为会话存储
场景:使用 MongoDB Replica Set 作为 Node.js 应用的会话存储,提高系统可用性。
解决方案:
部署架构:
- 3 个 MongoDB 节点(1 主 2 从)
- 节点分布在不同的可用区
安装依赖:
npm install express express-session connect-mongodb-session mongodb实现代码:
const express = require('express'); const session = require('express-session'); const MongoDBStore = require('connect-mongodb-session')(session); const { MongoClient } = require('mongodb'); const app = express(); // 复制集连接字符串 const uri = 'mongodb://admin:your_strong_password@server1:27017,server2:27017,server3:27017/admin?replicaSet=rs0'; // 创建 MongoDB 客户端 const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true }); // 连接到 MongoDB client.connect().then(() => { console.log('Connected to MongoDB Replica Set'); // 创建会话存储 const store = new MongoDBStore({ uri: uri, collection: 'sessions' }); // 处理存储错误 store.on('error', function(error) { console.error('Session store error:', error); }); // 配置会话中间件 app.use(session({ secret: 'your_secret_key', resave: false, saveUninitialized: false, store: store, cookie: { maxAge: 1000 * 60 * 60 * 24 // 24 小时 } })); // 测试路由 app.get('/', (req, res) => { if (req.session.views) { req.session.views++; res.send(`Views: ${req.session.views}`); } else { req.session.views = 1; res.send('First view!'); } }); // 启动服务器 app.listen(3000, () => { console.log('Server listening on port 3000'); }); }).catch(err => { console.error('Error connecting to MongoDB:', err); });使用示例:
- 启动应用:
node app.js - 访问
http://localhost:3000/,会看到 "First view!" - 刷新页面,会看到 "Views: 2"
- 重启应用,再次访问页面,会看到 "Views: 3"(会话数据已持久化)
- 启动应用:
7.3 实现 MongoDB 读写分离
场景:实现 MongoDB 的读写分离,提高系统整体性能。
解决方案:
部署架构:
- 3 个 MongoDB 节点(1 主 2 从)
实现代码:
const { MongoClient } = require('mongodb'); // 复制集连接字符串 const uri = 'mongodb://admin:your_strong_password@localhost:27017,localhost:27018,localhost:27019/admin?replicaSet=rs0'; // 创建客户端(用于写操作) const writeClient = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true }); // 创建客户端(用于读操作) const readClient = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, readPreference: 'secondary' // 优先从从节点读取 }); // 连接到 MongoDB async function connect() { try { await writeClient.connect(); await readClient.connect(); console.log('Connected to MongoDB Replica Set'); } catch (err) { console.error('Error connecting to MongoDB:', err); } } // 写操作 async function writeOperation() { try { const db = writeClient.db('app'); const result = await db.collection('users').insertOne({ name: '李四', age: 30, email: 'lisi@example.com' }); console.log('Write operation successful:', result.insertedId); return result.insertedId; } catch (err) { console.error('Error in write operation:', err); throw err; } } // 读操作 async function readOperation(userId) { try { const db = readClient.db('app'); const user = await db.collection('users').findOne({ _id: userId }); console.log('Read operation successful:', user); return user; } catch (err) { console.error('Error in read operation:', err); throw err; } } // 使用示例 async function main() { await connect(); // 执行写操作 const userId = await writeOperation(); // 执行读操作 const user = await readOperation(userId); // 关闭连接 await writeClient.close(); await readClient.close(); } main();测试验证:
- 启动应用:
node app.js - 查看日志输出,确认写操作和读操作都成功执行
- 查看 MongoDB 从节点的日志,确认读操作被路由到从节点
- 启动应用:
8. 总结
MongoDB Replica Set 是构建高可用 MongoDB 集群的理想解决方案,通过主从复制和自动故障转移机制,提供了比单机 MongoDB 更高的可用性和可靠性。本教程详细介绍了 MongoDB Replica Set 的核心概念、部署配置、使用方法和最佳实践,帮助开发者快速掌握 MongoDB Replica Set 的使用技巧,构建稳定可靠的 MongoDB 高可用集群。
在实际应用中,应根据具体需求和场景,合理配置 MongoDB Replica Set 参数,确保系统的稳定性和性能。同时,定期进行集群维护和监控,及时发现和解决问题,确保 MongoDB Replica Set 持续稳定运行。