1. MongoDB副本集架构概述

MongoDB副本集(Replica Set)是MongoDB实现高可用性的核心架构,通过数据冗余和自动故障转移来保证数据库的可用性和数据安全性。副本集架构是MongoDB生产环境部署的标准配置,本文将详细介绍副本集的架构原理、部署方法、运维管理技巧以及在企业级应用中的最佳实践。

1.1 副本集的核心价值

  1. 高可用性: 通过多副本保证服务的连续性
  2. 数据安全: 通过数据冗余保证数据的安全性
  3. 自动故障转移: 主节点故障时自动选举新的主节点
  4. 读写分离: 支持读写分离提升性能
  5. 数据一致性: 保证数据在多个副本间的一致性

1.2 副本集架构组件

  • 主节点(Primary): 处理所有写操作和读操作
  • 从节点(Secondary): 复制主节点数据,可处理读操作
  • 仲裁节点(Arbiter): 参与选举但不存储数据
  • 隐藏节点(Hidden): 不参与选举的从节点
  • 延迟节点(Delayed): 延迟同步的从节点

2. 副本集架构原理

2.1 副本集工作原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/bash
# MongoDB副本集架构原理说明
# @author 运维实战

echo "=== MongoDB副本集架构原理 ==="

cat << 'EOF'
MongoDB副本集工作原理:

1. 主节点(Primary)
- 处理所有写操作
- 处理读操作
- 将操作记录到oplog
- 响应从节点的复制请求

2. 从节点(Secondary)
- 从主节点复制数据
- 可以处理读操作
- 参与主节点选举
- 维护数据副本

3. 选举机制
- 使用Raft算法进行选举
- 需要大多数节点同意
- 自动检测主节点故障
- 自动选举新的主节点

4. 数据同步
- 基于oplog的异步复制
- 保证数据最终一致性
- 支持读写分离
- 支持延迟复制

5. 故障转移
- 自动检测节点故障
- 自动进行故障转移
- 保证服务连续性
- 最小化服务中断时间
EOF

echo "副本集架构原理说明完成"

2.2 副本集状态说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/bin/bash
# MongoDB副本集状态说明
# @author 运维实战

echo "=== MongoDB副本集状态说明 ==="

cat << 'EOF'
MongoDB副本集节点状态:

1. PRIMARY (1)
- 主节点状态
- 处理所有写操作
- 处理读操作
- 响应从节点请求

2. SECONDARY (2)
- 从节点状态
- 复制主节点数据
- 可以处理读操作
- 参与选举投票

3. RECOVERING (3)
- 恢复中状态
- 正在同步数据
- 不处理读写操作
- 不参与选举投票

4. STARTUP (0)
- 启动中状态
- 正在初始化
- 不处理读写操作
- 不参与选举投票

5. STARTUP2 (5)
- 启动中状态
- 正在加载数据
- 不处理读写操作
- 不参与选举投票

6. ARBITER (7)
- 仲裁节点状态
- 不存储数据
- 只参与选举投票
- 不处理读写操作

7. DOWN (6)
- 节点不可用
- 网络或服务故障
- 不处理读写操作
- 不参与选举投票

8. UNKNOWN (8)
- 状态未知
- 网络连接问题
- 需要进一步检查
- 不处理读写操作
EOF

echo "副本集状态说明完成"

3. 副本集部署配置

3.1 副本集部署脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
#!/bin/bash
# MongoDB副本集部署脚本
# @author 运维实战

# 配置变量
REPLICA_SET_NAME="rs0"
MONGO_PORT=27017
DATA_DIR="/data/mongodb"
LOG_DIR="/var/log/mongodb"
CONFIG_DIR="/etc/mongodb"
ADMIN_USER="admin"
ADMIN_PASS="admin123"

# 创建目录结构
create_directories() {
echo "创建MongoDB副本集目录结构..."

# 创建数据目录
mkdir -p $DATA_DIR/{primary,secondary1,secondary2}
chown -R mongodb:mongodb $DATA_DIR

# 创建日志目录
mkdir -p $LOG_DIR
chown -R mongodb:mongodb $LOG_DIR

# 创建配置目录
mkdir -p $CONFIG_DIR
chown -R mongodb:mongodb $CONFIG_DIR

echo "目录结构创建完成"
}

# 生成配置文件
generate_config_files() {
echo "生成MongoDB副本集配置文件..."

# 主节点配置文件
cat > $CONFIG_DIR/mongod-primary.conf << EOF
# MongoDB主节点配置文件
# @author 运维实战

# 存储配置
storage:
dbPath: $DATA_DIR/primary
journal:
enabled: true
commitIntervalMs: 100
wiredTiger:
engineConfig:
cacheSizeGB: 2
journalCompressor: snappy
directoryForIndexes: true
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true

# 网络配置
net:
port: $MONGO_PORT
bindIp: 0.0.0.0
maxIncomingConnections: 1000
compression:
compressors: snappy,zstd

# 日志配置
systemLog:
destination: file
path: $LOG_DIR/mongod-primary.log
logAppend: true
logRotate: reopen
verbosity: 1
component:
accessControl:
verbosity: 1
command:
verbosity: 1

# 进程管理
processManagement:
fork: true
pidFilePath: $DATA_DIR/primary/mongod.pid
timeZoneInfo: /usr/share/zoneinfo

# 副本集配置
replication:
replSetName: $REPLICA_SET_NAME
oplogSizeMB: 1024

# 安全配置
security:
authorization: enabled
keyFile: $CONFIG_DIR/keyfile

# 性能优化
operationProfiling:
slowOpThresholdMs: 100
mode: slowOp

# 监控配置
setParameter:
enableLocalhostAuthBypass: false
logLevel: 1
EOF

# 从节点1配置文件
cat > $CONFIG_DIR/mongod-secondary1.conf << EOF
# MongoDB从节点1配置文件
# @author 运维实战

# 存储配置
storage:
dbPath: $DATA_DIR/secondary1
journal:
enabled: true
commitIntervalMs: 100
wiredTiger:
engineConfig:
cacheSizeGB: 2
journalCompressor: snappy
directoryForIndexes: true
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true

# 网络配置
net:
port: $((MONGO_PORT + 1))
bindIp: 0.0.0.0
maxIncomingConnections: 1000
compression:
compressors: snappy,zstd

# 日志配置
systemLog:
destination: file
path: $LOG_DIR/mongod-secondary1.log
logAppend: true
logRotate: reopen
verbosity: 1
component:
accessControl:
verbosity: 1
command:
verbosity: 1

# 进程管理
processManagement:
fork: true
pidFilePath: $DATA_DIR/secondary1/mongod.pid
timeZoneInfo: /usr/share/zoneinfo

# 副本集配置
replication:
replSetName: $REPLICA_SET_NAME
oplogSizeMB: 1024

# 安全配置
security:
authorization: enabled
keyFile: $CONFIG_DIR/keyfile

# 性能优化
operationProfiling:
slowOpThresholdMs: 100
mode: slowOp

# 监控配置
setParameter:
enableLocalhostAuthBypass: false
logLevel: 1
EOF

# 从节点2配置文件
cat > $CONFIG_DIR/mongod-secondary2.conf << EOF
# MongoDB从节点2配置文件
# @author 运维实战

# 存储配置
storage:
dbPath: $DATA_DIR/secondary2
journal:
enabled: true
commitIntervalMs: 100
wiredTiger:
engineConfig:
cacheSizeGB: 2
journalCompressor: snappy
directoryForIndexes: true
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true

# 网络配置
net:
port: $((MONGO_PORT + 2))
bindIp: 0.0.0.0
maxIncomingConnections: 1000
compression:
compressors: snappy,zstd

# 日志配置
systemLog:
destination: file
path: $LOG_DIR/mongod-secondary2.log
logAppend: true
logRotate: reopen
verbosity: 1
component:
accessControl:
verbosity: 1
command:
verbosity: 1

# 进程管理
processManagement:
fork: true
pidFilePath: $DATA_DIR/secondary2/mongod.pid
timeZoneInfo: /usr/share/zoneinfo

# 副本集配置
replication:
replSetName: $REPLICA_SET_NAME
oplogSizeMB: 1024

# 安全配置
security:
authorization: enabled
keyFile: $CONFIG_DIR/keyfile

# 性能优化
operationProfiling:
slowOpThresholdMs: 100
mode: slowOp

# 监控配置
setParameter:
enableLocalhostAuthBypass: false
logLevel: 1
EOF

echo "配置文件生成完成"
}

# 生成密钥文件
generate_keyfile() {
echo "生成MongoDB副本集密钥文件..."

# 生成随机密钥
openssl rand -base64 756 > $CONFIG_DIR/keyfile
chmod 600 $CONFIG_DIR/keyfile
chown mongodb:mongodb $CONFIG_DIR/keyfile

echo "密钥文件生成完成"
}

# 启动MongoDB服务
start_mongodb_services() {
echo "启动MongoDB副本集服务..."

# 启动主节点
echo "启动主节点..."
mongod --config $CONFIG_DIR/mongod-primary.conf
sleep 5

# 启动从节点1
echo "启动从节点1..."
mongod --config $CONFIG_DIR/mongod-secondary1.conf
sleep 5

# 启动从节点2
echo "启动从节点2..."
mongod --config $CONFIG_DIR/mongod-secondary2.conf
sleep 5

echo "MongoDB副本集服务启动完成"
}

# 初始化副本集
initialize_replica_set() {
echo "初始化MongoDB副本集..."

# 连接到主节点并初始化副本集
mongo --port $MONGO_PORT << EOF
// 初始化MongoDB副本集
// @author 运维实战

// 初始化副本集配置
rs.initiate({
_id: "$REPLICA_SET_NAME",
members: [
{
_id: 0,
host: "localhost:$MONGO_PORT",
priority: 2,
tags: { "dc": "primary", "role": "primary" }
},
{
_id: 1,
host: "localhost:$((MONGO_PORT + 1))",
priority: 1,
tags: { "dc": "secondary", "role": "secondary" }
},
{
_id: 2,
host: "localhost:$((MONGO_PORT + 2))",
priority: 1,
tags: { "dc": "secondary", "role": "secondary" }
}
],
settings: {
heartbeatIntervalMillis: 2000,
electionTimeoutMillis: 10000,
catchUpTimeoutMillis: 2000,
catchUpTakeoverDelayMillis: 30000
}
});

// 等待副本集初始化完成
print("等待副本集初始化完成...");
while (rs.status().ok !== 1) {
sleep(1000);
}

// 检查副本集状态
print("副本集状态:");
rs.status();

print("副本集初始化完成");
EOF

echo "副本集初始化完成"
}

# 创建用户账户
create_users() {
echo "创建MongoDB用户账户..."

# 等待副本集稳定
sleep 10

# 创建管理员用户
mongo --port $MONGO_PORT << EOF
// 创建MongoDB用户账户
// @author 运维实战

use admin;

// 创建管理员用户
db.createUser({
user: "$ADMIN_USER",
pwd: "$ADMIN_PASS",
roles: [
{ role: "root", db: "admin" },
{ role: "clusterAdmin", db: "admin" },
{ role: "userAdminAnyDatabase", db: "admin" },
{ role: "dbAdminAnyDatabase", db: "admin" }
]
});

// 创建应用用户
use appdb;
db.createUser({
user: "appuser",
pwd: "app123",
roles: [
{ role: "readWrite", db: "appdb" },
{ role: "read", db: "appdb" }
]
});

// 创建只读用户
use appdb;
db.createUser({
user: "readonly",
pwd: "read123",
roles: [
{ role: "read", db: "appdb" }
]
});

print("用户账户创建完成");
EOF

echo "用户账户创建完成"
}

# 配置副本集优化
configure_replica_set_optimization() {
echo "配置副本集优化..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 配置副本集优化
// @author 运维实战

use admin;

// 配置副本集设置
var config = rs.conf();
config.settings.heartbeatIntervalMillis = 2000;
config.settings.electionTimeoutMillis = 10000;
config.settings.catchUpTimeoutMillis = 2000;
config.settings.catchUpTakeoverDelayMillis = 30000;

// 应用配置
rs.reconfig(config);

// 设置从节点读取偏好
rs.slaveOk();

// 配置读写分离
db.getMongo().setReadPref("secondaryPreferred");

print("副本集优化配置完成");
EOF

echo "副本集优化配置完成"
}

# 主函数
main() {
echo "开始部署MongoDB副本集..."

create_directories
generate_config_files
generate_keyfile
start_mongodb_services
initialize_replica_set
create_users
configure_replica_set_optimization

echo "MongoDB副本集部署完成!"
echo "主节点: localhost:$MONGO_PORT"
echo "从节点1: localhost:$((MONGO_PORT + 1))"
echo "从节点2: localhost:$((MONGO_PORT + 2))"
echo "管理员用户: $ADMIN_USER"
echo "管理员密码: $ADMIN_PASS"
}

# 执行主函数
main "$@"

3.2 副本集管理脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
#!/bin/bash
# MongoDB副本集管理脚本
# @author 运维实战

# 配置变量
REPLICA_SET_NAME="rs0"
MONGO_PORT=27017
ADMIN_USER="admin"
ADMIN_PASS="admin123"

# 检查副本集状态
check_replica_set_status() {
echo "检查MongoDB副本集状态..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 检查副本集状态
// @author 运维实战

use admin;

// 显示副本集状态
print("=== 副本集状态 ===");
var status = rs.status();
printjson(status);

// 显示副本集配置
print("\n=== 副本集配置 ===");
var config = rs.conf();
printjson(config);

// 显示副本集健康状态
print("\n=== 副本集健康状态 ===");
rs.printSlaveReplicationInfo();

// 显示副本集统计信息
print("\n=== 副本集统计信息 ===");
rs.printReplicationInfo();

// 显示节点状态
print("\n=== 节点状态 ===");
var members = status.members;
for (var i = 0; i < members.length; i++) {
var member = members[i];
print("节点 " + member.name + " 状态: " + member.stateStr + " (优先级: " + member.priority + ")");
}

// 显示主节点信息
print("\n=== 主节点信息 ===");
var primary = rs.isMaster();
print("主节点: " + primary.primary);
print("副本集名称: " + primary.setName);
print("副本集版本: " + primary.setVersion);
EOF

echo "副本集状态检查完成"
}

# 添加副本集成员
add_replica_set_member() {
local host=$1
local port=$2
local priority=${3:-1}
local tags=${4:-""}

echo "添加副本集成员: $host:$port (优先级: $priority)..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 添加副本集成员
// @author 运维实战

use admin;

// 添加新成员
var memberConfig = {
host: "$host:$port",
priority: $priority
};

if ("$tags" !== "") {
memberConfig.tags = $tags;
}

rs.add(memberConfig);

// 等待成员同步
print("等待新成员加入...");
while (rs.status().members.length < 4) {
sleep(1000);
}

print("新成员添加完成");
EOF

echo "副本集成员添加完成"
}

# 移除副本集成员
remove_replica_set_member() {
local host=$1
local port=$2

echo "移除副本集成员: $host:$port..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 移除副本集成员
// @author 运维实战

use admin;

// 移除成员
rs.remove("$host:$port");

print("成员移除完成");
EOF

echo "副本集成员移除完成"
}

# 重新配置副本集
reconfigure_replica_set() {
echo "重新配置副本集..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 重新配置副本集
// @author 运维实战

use admin;

// 获取当前配置
var config = rs.conf();

// 修改配置
config.members[0].priority = 2;
config.members[1].priority = 1;
config.members[2].priority = 1;

// 更新设置
config.settings.heartbeatIntervalMillis = 2000;
config.settings.electionTimeoutMillis = 10000;
config.settings.catchUpTimeoutMillis = 2000;
config.settings.catchUpTakeoverDelayMillis = 30000;

// 应用新配置
rs.reconfig(config);

print("副本集重新配置完成");
EOF

echo "副本集重新配置完成"
}

# 强制重新选举主节点
force_election() {
echo "强制重新选举主节点..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 强制重新选举主节点
// @author 运维实战

use admin;

// 强制重新选举
rs.stepDown();

// 等待选举完成
print("等待选举完成...");
while (rs.isMaster().ismaster !== true) {
sleep(1000);
}

print("主节点重新选举完成");
EOF

echo "主节点重新选举完成"
}

# 设置从节点为隐藏节点
set_hidden_member() {
local host=$1
local port=$2

echo "设置从节点为隐藏节点: $host:$port..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 设置从节点为隐藏节点
// @author 运维实战

use admin;

// 获取当前配置
var config = rs.conf();

// 找到要隐藏的成员
for (var i = 0; i < config.members.length; i++) {
if (config.members[i].host === "$host:$port") {
config.members[i].hidden = true;
config.members[i].priority = 0;
break;
}
}

// 应用配置
rs.reconfig(config);

print("隐藏节点设置完成");
EOF

echo "隐藏节点设置完成"
}

# 设置延迟节点
set_delayed_member() {
local host=$1
local port=$2
local delay_seconds=${3:-3600}

echo "设置延迟节点: $host:$port (延迟: $delay_seconds秒)..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 设置延迟节点
// @author 运维实战

use admin;

// 获取当前配置
var config = rs.conf();

// 找到要设置延迟的成员
for (var i = 0; i < config.members.length; i++) {
if (config.members[i].host === "$host:$port") {
config.members[i].priority = 0;
config.members[i].hidden = true;
config.members[i].slaveDelay = $delay_seconds;
break;
}
}

// 应用配置
rs.reconfig(config);

print("延迟节点设置完成");
EOF

echo "延迟节点设置完成"
}

# 备份副本集
backup_replica_set() {
local backup_dir=$1
local backup_name=$(date +%Y%m%d_%H%M%S)

echo "备份副本集到: $backup_dir/$backup_name..."

# 创建备份目录
mkdir -p $backup_dir/$backup_name

# 执行备份
mongodump --host localhost:$MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin --out $backup_dir/$backup_name

echo "副本集备份完成"
}

# 恢复副本集
restore_replica_set() {
local backup_dir=$1
local backup_name=$2

echo "从备份恢复副本集: $backup_dir/$backup_name..."

# 执行恢复
mongorestore --host localhost:$MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin $backup_dir/$backup_name

echo "副本集恢复完成"
}

# 主函数
main() {
case $1 in
"status")
check_replica_set_status
;;
"add")
add_replica_set_member $2 $3 $4 $5
;;
"remove")
remove_replica_set_member $2 $3
;;
"reconfig")
reconfigure_replica_set
;;
"election")
force_election
;;
"hidden")
set_hidden_member $2 $3
;;
"delayed")
set_delayed_member $2 $3 $4
;;
"backup")
backup_replica_set $2
;;
"restore")
restore_replica_set $2 $3
;;
*)
echo "用法: $0 {status|add|remove|reconfig|election|hidden|delayed|backup|restore}"
echo " status - 检查副本集状态"
echo " add <host> <port> [priority] [tags] - 添加副本集成员"
echo " remove <host> <port> - 移除副本集成员"
echo " reconfig - 重新配置副本集"
echo " election - 强制重新选举主节点"
echo " hidden <host> <port> - 设置隐藏节点"
echo " delayed <host> <port> [delay_seconds] - 设置延迟节点"
echo " backup <backup_dir> - 备份副本集"
echo " restore <backup_dir> <backup_name> - 恢复副本集"
;;
esac
}

# 执行主函数
main "$@"

4. 副本集监控和故障处理

4.1 副本集监控脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#!/bin/bash
# MongoDB副本集监控脚本
# @author 运维实战

# 配置变量
MONGO_PORT=27017
ADMIN_USER="admin"
ADMIN_PASS="admin123"
LOG_FILE="/var/log/mongodb/replica_monitor.log"
ALERT_EMAIL="admin@example.com"

# 记录日志
log_message() {
local message=$1
echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> $LOG_FILE
}

# 检查副本集健康状态
check_replica_set_health() {
echo "检查副本集健康状态..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 检查副本集健康状态
// @author 运维实战

use admin;

// 获取副本集状态
var status = rs.status();
var members = status.members;

// 检查每个节点的状态
for (var i = 0; i < members.length; i++) {
var member = members[i];
var state = member.state;
var name = member.name;

if (state === 1) {
print("主节点 " + name + " 状态正常");
} else if (state === 2) {
print("从节点 " + name + " 状态正常");
} else {
print("节点 " + name + " 状态异常: " + state);
}
}

// 检查复制延迟
var replicationInfo = rs.printSlaveReplicationInfo();
print("复制信息: " + replicationInfo);

// 检查oplog大小
var oplogStats = db.oplog.rs.stats();
print("Oplog大小: " + oplogStats.size + " bytes");
print("Oplog使用率: " + (oplogStats.storageSize / oplogStats.maxSize * 100).toFixed(2) + "%");
EOF

log_message "副本集健康状态检查完成"
}

# 检查复制延迟
check_replication_lag() {
echo "检查复制延迟..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 检查复制延迟
// @author 运维实战

use admin;

// 获取副本集状态
var status = rs.status();
var members = status.members;

// 检查每个从节点的复制延迟
for (var i = 0; i < members.length; i++) {
var member = members[i];
if (member.state === 2) { // 从节点
var lag = member.optimeDate - member.lastHeartbeat;
print("从节点 " + member.name + " 复制延迟: " + lag + " ms");

if (lag > 10000) { // 延迟超过10秒
print("警告: 从节点 " + member.name + " 复制延迟过高: " + lag + " ms");
}
}
}
EOF

log_message "复制延迟检查完成"
}

# 检查oplog使用情况
check_oplog_usage() {
echo "检查oplog使用情况..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 检查oplog使用情况
// @author 运维实战

use local;

// 获取oplog统计信息
var oplogStats = db.oplog.rs.stats();
print("Oplog统计信息:");
print(" 总大小: " + oplogStats.size + " bytes");
print(" 存储大小: " + oplogStats.storageSize + " bytes");
print(" 最大大小: " + oplogStats.maxSize + " bytes");
print(" 使用率: " + (oplogStats.storageSize / oplogStats.maxSize * 100).toFixed(2) + "%");

// 检查oplog时间范围
var firstEntry = db.oplog.rs.find().sort({ts: 1}).limit(1).next();
var lastEntry = db.oplog.rs.find().sort({ts: -1}).limit(1).next();
var timeRange = lastEntry.ts.getTime() - firstEntry.ts.getTime();
print("Oplog时间范围: " + (timeRange / 1000 / 3600).toFixed(2) + " 小时");

// 检查oplog写入速率
var recentEntries = db.oplog.rs.find({ts: {\$gte: new Date(Date.now() - 60000)}}).count();
print("最近1分钟oplog写入: " + recentEntries + " 条");
EOF

log_message "Oplog使用情况检查完成"
}

# 检查网络连接
check_network_connections() {
echo "检查网络连接..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 检查网络连接
// @author 运维实战

use admin;

// 获取服务器状态
var serverStatus = db.serverStatus();
var connections = serverStatus.connections;
print("连接统计:");
print(" 当前连接数: " + connections.current);
print(" 可用连接数: " + connections.available);
print(" 总连接数: " + connections.totalCreated);

// 检查网络统计
var network = serverStatus.network;
print("网络统计:");
print(" 字节输入: " + network.bytesIn);
print(" 字节输出: " + network.bytesOut);
print(" 请求数: " + network.numRequests);
EOF

log_message "网络连接检查完成"
}

# 检查磁盘空间
check_disk_space() {
echo "检查磁盘空间..."

# 检查数据目录磁盘空间
local data_dir="/data/mongodb"
local disk_usage=$(df -h $data_dir | awk 'NR==2 {print $5}' | sed 's/%//')

if [ $disk_usage -gt 80 ]; then
echo "磁盘空间不足: $disk_usage%"
log_message "磁盘空间不足: $disk_usage%"
send_alert "MongoDB副本集磁盘空间不足: $disk_usage%"
else
echo "磁盘空间正常: $disk_usage%"
log_message "磁盘空间正常: $disk_usage%"
fi
}

# 检查日志文件
check_log_files() {
echo "检查日志文件..."

local log_dir="/var/log/mongodb"
local error_count=$(grep -c "ERROR" $log_dir/*.log 2>/dev/null || echo "0")
local warning_count=$(grep -c "WARNING" $log_dir/*.log 2>/dev/null || echo "0")

if [ $error_count -gt 0 ]; then
echo "发现 $error_count 个错误日志"
log_message "发现 $error_count 个错误日志"
send_alert "MongoDB副本集发现 $error_count 个错误日志"
fi

if [ $warning_count -gt 10 ]; then
echo "发现 $warning_count 个警告日志"
log_message "发现 $warning_count 个警告日志"
send_alert "MongoDB副本集发现 $warning_count 个警告日志"
fi

echo "日志检查完成"
}

# 发送告警
send_alert() {
local message=$1
echo "发送告警: $message"

# 发送邮件告警
echo "$message" | mail -s "MongoDB副本集告警" $ALERT_EMAIL

# 发送短信告警(需要配置短信服务)
# curl -X POST "https://api.sms.com/send" -d "message=$message"

log_message "告警发送完成: $message"
}

# 生成监控报告
generate_monitor_report() {
echo "生成监控报告..."

local report_file="/var/log/mongodb/replica_monitor_report_$(date +%Y%m%d).txt"

cat > $report_file << EOF
MongoDB副本集监控报告
生成时间: $(date)
========================================

1. 副本集健康状态检查
$(check_replica_set_health)

2. 复制延迟检查
$(check_replication_lag)

3. Oplog使用情况检查
$(check_oplog_usage)

4. 网络连接检查
$(check_network_connections)

5. 磁盘空间检查
$(check_disk_space)

6. 日志文件检查
$(check_log_files)

========================================
EOF

echo "监控报告生成完成: $report_file"
log_message "监控报告生成完成: $report_file"
}

# 主函数
main() {
echo "开始MongoDB副本集监控..."

check_replica_set_health
check_replication_lag
check_oplog_usage
check_network_connections
check_disk_space
check_log_files
generate_monitor_report

echo "MongoDB副本集监控完成"
}

# 执行主函数
main "$@"

4.2 故障处理脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#!/bin/bash
# MongoDB副本集故障处理脚本
# @author 运维实战

# 配置变量
MONGO_PORT=27017
ADMIN_USER="admin"
ADMIN_PASS="admin123"
LOG_FILE="/var/log/mongodb/fault_handling.log"

# 记录日志
log_message() {
local message=$1
echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> $LOG_FILE
}

# 检测主节点故障
detect_primary_failure() {
echo "检测主节点故障..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 检测主节点故障
// @author 运维实战

use admin;

// 获取副本集状态
var status = rs.status();
var members = status.members;

// 检查是否有主节点
var hasPrimary = false;
for (var i = 0; i < members.length; i++) {
if (members[i].state === 1) {
hasPrimary = true;
print("主节点正常: " + members[i].name);
break;
}
}

if (!hasPrimary) {
print("警告: 没有主节点,需要手动干预");
// 这里可以添加自动故障转移逻辑
}
EOF

log_message "主节点故障检测完成"
}

# 处理从节点故障
handle_secondary_failure() {
local failed_host=$1
local failed_port=$2

echo "处理从节点故障: $failed_host:$failed_port..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 处理从节点故障
// @author 运维实战

use admin;

// 获取副本集状态
var status = rs.status();
var members = status.members;

// 找到故障节点
for (var i = 0; i < members.length; i++) {
if (members[i].host === "$failed_host:$failed_port") {
if (members[i].state === 6) { // DOWN状态
print("发现故障节点: " + members[i].name);

// 移除故障节点
rs.remove("$failed_host:$failed_port");
print("故障节点已移除");
}
break;
}
}
EOF

log_message "从节点故障处理完成: $failed_host:$failed_port"
}

# 处理网络分区
handle_network_partition() {
echo "处理网络分区..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 处理网络分区
// @author 运维实战

use admin;

// 获取副本集状态
var status = rs.status();
var members = status.members;

// 检查网络连接
for (var i = 0; i < members.length; i++) {
var member = members[i];
if (member.state === 8) { // UNKNOWN状态
print("发现网络分区节点: " + member.name);

// 尝试重新连接
try {
rs.reconfig(rs.conf());
print("尝试重新配置副本集");
} catch (e) {
print("重新配置失败: " + e);
}
}
}
EOF

log_message "网络分区处理完成"
}

# 处理数据不一致
handle_data_inconsistency() {
echo "处理数据不一致..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 处理数据不一致
// @author 运维实战

use admin;

// 获取副本集状态
var status = rs.status();
var members = status.members;

// 检查数据一致性
for (var i = 0; i < members.length; i++) {
var member = members[i];
if (member.state === 2) { // 从节点
var lag = member.optimeDate - member.lastHeartbeat;
if (lag > 30000) { // 延迟超过30秒
print("发现数据不一致节点: " + member.name + " 延迟: " + lag + " ms");

// 建议重新同步
print("建议重新同步节点: " + member.name);
}
}
}
EOF

log_message "数据不一致处理完成"
}

# 处理oplog溢出
handle_oplog_overflow() {
echo "处理oplog溢出..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 处理oplog溢出
// @author 运维实战

use local;

// 获取oplog统计信息
var oplogStats = db.oplog.rs.stats();
var usage = oplogStats.storageSize / oplogStats.maxSize;

if (usage > 0.9) { // 使用率超过90%
print("警告: Oplog使用率过高: " + (usage * 100).toFixed(2) + "%");

// 建议增加oplog大小
print("建议增加oplog大小或减少写入频率");
}
EOF

log_message "Oplog溢出处理完成"
}

# 自动故障转移
auto_failover() {
echo "执行自动故障转移..."

mongo --port $MONGO_PORT -u $ADMIN_USER -p $ADMIN_PASS --authenticationDatabase admin << EOF
// 自动故障转移
// @author 运维实战

use admin;

// 获取副本集状态
var status = rs.status();
var members = status.members;

// 检查是否有主节点
var hasPrimary = false;
for (var i = 0; i < members.length; i++) {
if (members[i].state === 1) {
hasPrimary = true;
break;
}
}

if (!hasPrimary) {
print("没有主节点,尝试自动故障转移");

// 找到可用的从节点
for (var i = 0; i < members.length; i++) {
if (members[i].state === 2) {
print("尝试将节点 " + members[i].name + " 提升为主节点");

// 这里需要手动干预,因为自动故障转移需要大多数节点同意
print("需要手动干预或等待自动选举");
break;
}
}
}
EOF

log_message "自动故障转移完成"
}

# 主函数
main() {
case $1 in
"detect-primary")
detect_primary_failure
;;
"handle-secondary")
handle_secondary_failure $2 $3
;;
"handle-network")
handle_network_partition
;;
"handle-data")
handle_data_inconsistency
;;
"handle-oplog")
handle_oplog_overflow
;;
"auto-failover")
auto_failover
;;
*)
echo "用法: $0 {detect-primary|handle-secondary|handle-network|handle-data|handle-oplog|auto-failover}"
echo " detect-primary - 检测主节点故障"
echo " handle-secondary <host> <port> - 处理从节点故障"
echo " handle-network - 处理网络分区"
echo " handle-data - 处理数据不一致"
echo " handle-oplog - 处理oplog溢出"
echo " auto-failover - 自动故障转移"
;;
esac
}

# 执行主函数
main "$@"

5. 总结

5.1 MongoDB副本集最佳实践

  1. 合理规划架构: 根据业务需求选择合适的副本集配置
  2. 高可用设计: 使用奇数个节点保证选举的稳定性
  3. 读写分离: 合理配置读写分离提升性能
  4. 监控告警: 建立完善的监控和告警机制
  5. 备份恢复: 制定完善的备份和恢复策略

5.2 性能优化建议

  • oplog优化: 合理设置oplog大小
  • 网络优化: 优化网络配置和连接池
  • 存储优化: 选择合适的存储引擎和压缩算法
  • 内存配置: 合理配置内存使用
  • 索引优化: 优化索引设计和查询性能

5.3 运维管理要点

  • 日常监控: 定期检查副本集状态和性能
  • 故障处理: 建立故障处理流程和应急预案
  • 容量规划: 根据业务增长规划副本集容量
  • 安全加固: 加强副本集安全配置和访问控制
  • 文档管理: 维护完善的运维文档和操作手册

通过本文的MongoDB副本集架构运维实战指南,您可以掌握MongoDB副本集的架构原理、部署方法、监控技巧和故障处理策略,构建高可用、高性能的MongoDB副本集系统!