MongoDB学习笔记(五)- MongoDB复制集读写事务

文章目录

内容来自唐建法(TJ)老师的MongoDB 高手课 (geekbang.org)

单机版三节点副本集搭建

在一台Ubuntu机器上,通过配置文件的方式启动三个mongod实例。各个参数的说明请参考Configuration File Options — MongoDB Manual。示例配置文件和说明如下:

 1systemLog:
 2  destination: file
 3  path: /data/dbx/mongod.log # x替换成1,2,3对应三个不同的mongod实例
 4  logAppend: true
 5storage:
 6  dbPath: /data/dbx # x替换成1,2,3对应三个不同的mongod实例
 7net:
 8  bindIp: 0.0.0.0 # 允许远程连接
 9  port: xxxx   # 三个实例使用端口依次为28017,28018,28019
10replication:
11  replSetName: jonlimxrs # rs的名字,可根据需要命名
12processManagement:
13  fork: true

启动三个mongod实例:

1mongod -f /data/db1/mongod.conf
2mongod -f /data/db2/mongod.conf
3mongod -f /data/db3/mongod.conf
4
5➜  ~ ps -ef | grep mongod
6root        1719       1  0 Feb16 ?        00:10:13 mongod -f ./mongod.conf
7root        1771       1  0 Feb16 ?        00:10:18 mongod -f ../db2/mongod.conf
8root        1823       1  0 Feb16 ?        00:10:13 mongod -f ../db3/mongod.conf
9ability     5595    5477  0 10:20 pts/0    00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox mongod

连接第一个实例,然后在第一个实例上初始化副本集:

1  ~ mongo --port 28017
2> rs.initiate({
3    _id: "jonlimxrs",
4    members: [{_id: 0,host: "192.168.50.27:28017" },
5              {_id: 1,host: "192.168.50.27:28018" },
6              {_id: 2,host: "192.168.50.27:28019" }]}) // 这里我使用的IP,生产环境应该使用DNS而不是IP

初始化完毕后,需要在所有的SECONDARY节点上启用读操作:

 1jonlimxrs:SECONDARY> db.test.find()
 2Error: error: {
 3	"topologyVersion" : {
 4		"processId" : ObjectId("620cb55a0e2488713ea2053f"),
 5		"counter" : NumberLong(4)
 6	},
 7	"ok" : 0,
 8	"errmsg" : "not master and slaveOk=false",
 9	"code" : 13435,
10	"codeName" : "NotPrimaryNoSecondaryOk", // 默认不允许读操作
11	"$clusterTime" : {
12		"clusterTime" : Timestamp(1645066026, 4),
13		"signature" : {
14			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
15			"keyId" : NumberLong(0)
16		}
17	},
18	"operationTime" : Timestamp(1645066026, 4)
19}
20
21jonlimxrs:SECONDARY> rs.secondaryOk() // 启用读操作

writeConcern介绍

1{ w: <value>, j: <boolean>, wtimeout: <number> }

在插入、更新和删除操作的时候可以指定writeConcern,意义在意该操作在个w节点上完成才算成功,否则操作被阻塞。

参数说明
w节点的个数。可以指定具体的数,但是不能超过总的节点数。通常指定为majority,有投票资格的节点个数的一半(向下取整)加1
j如果是true说明操作需要落盘(journal文件)
wtimeout操作超时时间,单位是毫秒。如果不指定该参数,或者值设置为0,那么在正常返回之前,相应的操作将一直阻塞

详细介绍请参看Write Concern — MongoDB Manual

现在通过设置SECONDARY的延时来模拟异常情况,来观察writeConcern的作用:

 1jonlimxrs:PRIMARY> conf=rs.conf()
 2jonlimxrs:PRIMARY> conf.members[1].secondaryDelaySecs=5 // 设置数据同步延迟时间,单位秒
 3jonlimxrs:PRIMARY> conf.members[1].priority=0 // 设置为0是的不能被选举成为Primary
 4jonlimxrs:PRIMARY> rs.reconfig(conf) 
 5{
 6    "_id": 1,
 7    "host": "192.168.50.27:28018",
 8    "arbiterOnly": false,
 9    "buildIndexes": true,
10    "hidden": false,
11    "priority": 0,
12    "tags": {},
13    "secondaryDelaySecs": 5,
14    "votes": 1
15}
16
17jonlimxrs:PRIMARY> db.test.insertOne({count:1000},{writeConcern:{w: 3}})
18// 大约5秒后返回
19{
20	"acknowledged" : true,
21	"insertedId" : ObjectId("620f5fad9a5fd7bf5a122785")
22}

readPreference和readPreferenceTags

readPreference决定了从哪一个节点读取数据,可配置的值如下表。根据场景的不同选择从不同的节点读取数据。

选项说明
primary只从主节点读取
primaryPreferred优先从主节点读取,如果不可用就从从节点读取
secondary只从从节点读取
secondaryPreferred优先从从节点读取,如果不可用就从主节点读取
nearest从时延最小的节点读取

关于readPreference的更多介绍请参考:Read Preference — MongoDB Manual

readPreferenceTags与readPreference类似,通过对节点打上特定的标签,读取的时候可以选择从具有特定标签的节点读取。详见:Read Preference Tag Sets — MongoDB Manual

连接字符串上设置这两个参数的方式:Connection String URI Format — MongoDB Manual

现在通过db.fsyncLock() 锁定从节点写入来模拟从节点不可用的情况:

1// 分别在两个从节点执行db.fsyncLock()
2// 192.168.50.27:28018
3jonlimxrs:SECONDARY> db.fsyncLock()
4// 192.168.50.27:28019
5jonlimxrs:SECONDARY> db.fsyncLock()
6jonlimxrs:PRIMARY> db.test.insertOne({a:1001},{wtimeout:1000}) // 主节点插入
7// 从节点上查不到数据
8jonlimxrs:SECONDARY> db.test.find({a:1001}).readPref("secondary")