耿健的个人博客

一个即将放飞理想的咸鱼博主

0%

47.配置mongodb副本集的翻车笔记

聊聊起源

在自己练手的小玩具功能不断复杂之后,
开始出现一个请求需要操作多个表的场景.

面对这个情况,
考虑到如果操作一个数据库的时候失败报错, 那之前操作的还需要手动恢复,
感觉会给业务代码里面掺杂很多复杂逻辑.
不利于后续开发,

经过研究之后, 发现事务回滚机制正好试用这个场景,
那高低研究一波.

代码改造

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
const mongoose = require("mongoose");

// 创建会话
const session = await mongoose.startSession();

try {
// 开始会话
session.startTransaction();
// do something...
// const resTry = await funTry?.();

// 提交会话
await session.commitTransaction();

return resTry;
} catch (error) {
// 会话报错终止, 该会话内操作开始回滚
session.abortTransaction();

// do catch something...
// const resCatch = await funCatch?.(error);
return resCatch;
} finally {
// 会话结束
session.endSession();
}

代码改造后测试了一下,
当抛出异常, 代码也执行了 session.abortTransaction,
不过之前写入数据库的数据并没有撤销.
经过查询资料之后发现,
mongodb 想要事务回滚必须要设置为副本集模式.

配置改造

登录服务器执行如下命令

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
// 创建一个名为 "mongodbnet" 的 Docker 网络,可以将多个容器连接到同一个网络中,使它们能够相互通信。
docker network create mongodbnet

// 查看 Docker 中的所有网络
docker network ls

// 创建 conf/mongod.keyfile (为副本集共用)
openssl rand -base64 756 > /usr/local/mongo/conf/mongod.keyfile

// 创建 conf/mongod.conf (为副本集共用)
vim /usr/local/mongo/conf/mongod.conf

#

security:
authorization: enabled
keyFile: /conf/mongod.keyfile

#

replication:
replSetName: rs0

#

net:
port: 27017
bindIp: 127.0.0.1

// 创建 mongod.conf.orig(为副本集共用)

#

security:
authorization: enabled
keyFile: /conf/mongod.keyfile

#

replication:
replSetName: rs0

// 放开权限
chmod 600 /usr/local/mongo/conf/mongod.keyfile

// 启动容器
docker run \
--name mongo1 \
--net mongodbnet \
-p 27001:27017 \
-v /usr/local/mongo/mongod.conf.orig:/etc/mongod.conf.orig \
-v /usr/local/mongo/conf/mongod.conf:/conf/mongod.conf \
-v /usr/local/mongo/conf/mongod.keyfile:/conf/mongod.keyfile \
-v /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime \
-e MONGO_INITDB_ROOT_USERNAME=yourusername \
-e MONGO_INITDB_ROOT_PASSWORD=yourpassword \
-d --restart=always mongo:4.2 \
--replSet rs0 \
--auth

docker run \
--name mongo2 \
--net mongodbnet \
-p 27002:27017 \
-v /usr/local/mongo/mongod.conf.orig:/etc/mongod.conf.orig \
-v /usr/local/mongo/conf/mongod.conf:/conf/mongod.conf \
-v /usr/local/mongo/conf/mongod.keyfile:/conf/mongod.keyfile \
-v /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime \
-e MONGO_INITDB_ROOT_USERNAME=yourusername \
-e MONGO_INITDB_ROOT_PASSWORD=yourpassword \
-d --restart=always mongo:4.2 \
--replSet rs0 \
--auth

docker run \
--name mongo3 \
--net mongodbnet \
-p 27003:27017 \
-v /usr/local/mongo/mongod.conf.orig:/etc/mongod.conf.orig \
-v /usr/local/mongo/conf/mongod.conf:/conf/mongod.conf \
-v /usr/local/mongo/conf/mongod.keyfile:/conf/mongod.keyfile \
-v /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime \
-e MONGO_INITDB_ROOT_USERNAME=yourusername \
-e MONGO_INITDB_ROOT_PASSWORD=yourpassword \
-d --restart=always mongo:4.2 \
--replSet rs0 \
--auth

// 考虑关闭三个 mongodb 容器,然后按照先开启主节点容器、再开启父节点容器的顺序启动。(选做)
docker stop mongo3 mongo2 mongo1

docker restart mongo1
docker restart mongo2
docker restart mongo3

// 进入主 mongo 容器
docker exec -it mongo1 bash

chmod 600 /conf/mongod.keyfile

// 查看通用配置文件
cat etc/mongod.conf.orig

// 进入 mongo 数据库
mongo -u yourusername -p yourpassword --authenticationDatabase admin

// 初始化副本集

> rs.initiate()
> rs.initiate({ \_id: "rs0", members: [ { _id: 0, host: "mongo1:27017" }, { _id: 1, host: "mongo2:27017" }, { _id: 2, host: "mongo3:27017" } ]})

rs0:PRIMARY> rs.status()
rs0:PRIMARY> rs.isMaster()

// 关联副本
rs0:PRIMARY> rs.add("mongo2:27017")
rs0:PRIMARY> rs.add("mongo3:27017")

// ===== 备份操作 =====
// 启动容器
-v /usr/local/mongo/conf/mongod.conf:/conf/mongod.conf \
-v /usr/local/mongo/mongod.conf.orig:/etc/mongod.conf.orig \
--keyFile /conf/mongod.keyfile \
--config /conf/mongod.conf \

// 矫正时区
docker cp /usr/share/zoneinfo/Asia/Shanghai mongo1:/etc/localtime
docker cp /usr/share/zoneinfo/Asia/Shanghai mongo2:/etc/localtime
docker cp /usr/share/zoneinfo/Asia/Shanghai mongo3:/etc/localtime

// 容器内操作配置
mongo --config /conf/mongod.conf --keyFile /conf/mongod.keyfile --replSet rs0

// 需要携带账号密码么?
rs.add("mongo2:27017", { username: "yourusername", password: "yourpassword", authenticationDatabase: "admin" })
// =================

库库一顿配置,
不好使…
裂开了,
后面再说吧

数据迁移

后面如果副本集配置好之后,
那么就需要将旧的数据库数据迁移到新的副本集数据库之中.
操作待学习…

后续

事务回滚的优先级不是那么高,
先暂时搁置一波了,
太浪费时间了.
真在实际中遇到这种问题,
到时候再抱住运维大哥大腿好了.
那么再完善这个机制好了.