第38章:MongoDB-集群--Replica Sets(副本集)---多机的搭建

时间:2021-11-16 14:15:14
①机器环境

182.48.115.236    master-node(主节点)

182.48.115.237    slave-node1(从节点)

182.48.115.238    slave-node2(从节点)

MongoDB 安装目录:/usr/local/mongodb

MongoDB 数据库目录:/usr/local/mongodb/data

MongoDB 日志目录:/usr/local/mongodb/log/mongo.log

MongoDB 配置文件:/usr/local/mongodb/mongodb.conf

对以上三台服务器部署Mongodb的副本集功能,定义副本集名称为:hqmongodb

关闭三台服务器的iptables防火墙和selinux

②配置文件

确保三台副本集服务器上的配置文件完全相同(即三台机器的mongodb.conf配置一样,除了配置文件中绑定的ip不一样)。

下面操作在三台节点机上都要执行:

port=27017

bind_ip = 182.48.115.236     //这个最好配置成本机的ip地址。否则后面进行副本集初始化的时候可能会失败!

dbpath=/usr/local/mongodb/data

logpath=/usr/local/mongodb/log/mongo.log

pidfilepath=/usr/local/mongodb/mongo.pid

fork=true

logappend=true

shardsvr=true

directoryperdb=true

replSet=hqmongodb           //副本集名称为:hqmongodb

分别启动mongodb

[root@master-node ~]# ./mongod --config=mongodb.conf

③对master-node主节点进行配置
//其实,刚开始这三个节点中的任何一个都可以用来初始化为开始的主节点。这里选择以master-node为主节点

[root@master-node ~]# ./mongo 182.48.115.236:27017        //登陆到mongodb数据库中执行下面命令操作。由于配置文件中绑定了ip,所以要用这个绑定的ip登陆

3.1)初始化副本集,设置本机为主节点 PRIMARY

> rs.initiate()

{

"info2" : "no configuration specified. Using a default configuration for the set",

"me" : "182.48.115.236:27017",

"ok" : 1

}

hqmongodb:OTHER> rs.conf()

{

"_id" : "hqmongodb",

"version" : 1,

"protocolVersion" : NumberLong(1),

"members" : [

{

"_id" : 0,

"host" : "182.48.115.236:27017",

"arbiterOnly" : false,

"buildIndexes" : true,

"hidden" : false,

"priority" : 1,

"tags" : {

},

"slaveDelay" : NumberLong(0),

"votes" : 1

}

],

"settings" : {

"chainingAllowed" : true,

"heartbeatIntervalMillis" : 2000,

"heartbeatTimeoutSecs" : 10,

"electionTimeoutMillis" : 10000,

"catchUpTimeoutMillis" : 2000,

"getLastErrorModes" : {

},

"getLastErrorDefaults" : {

"w" : 1,

"wtimeout" : 0

},

"replicaSetId" : ObjectId("5932f142a55dc83eca86ea86")

}

}

3.2)添加副本集从节点。(发现在执行上面的两个命令后,前缀已经改成"hqmongodb:PRIMARY"了,即已经将其自己设置为主节点 PRIMARY了)

hqmongodb:PRIMARY> rs.add("182.48.115.237:27017")

{ "ok" : 1 }

hqmongodb:PRIMARY> rs.add("182.48.115.238:27017")

{ "ok" : 1 }

3.3)设置节点优先级

hqmongodb:PRIMARY> cfg = rs.conf()          //查看节点顺序

{

"_id" : "hqmongodb",

"version" : 3,

"protocolVersion" : NumberLong(1),

"members" : [

{

"_id" : 0,

"host" : "182.48.115.236:27017",

"arbiterOnly" : false,

"buildIndexes" : true,

"hidden" : false,

"priority" : 1,

"tags" : {

},

"slaveDelay" : NumberLong(0),

"votes" : 1

},

{

"_id" : 1,

"host" : "182.48.115.237:27017",

"arbiterOnly" : false,

"buildIndexes" : true,

"hidden" : false,

"priority" : 1,

"tags" : {

},

"slaveDelay" : NumberLong(0),

"votes" : 1

},

{

"_id" : 2,

"host" : "182.48.115.238:27017",

"arbiterOnly" : false,

"buildIndexes" : true,

"hidden" : false,

"priority" : 1,

"tags" : {

},

"slaveDelay" : NumberLong(0),

"votes" : 1

}

],

"settings" : {

"chainingAllowed" : true,

"heartbeatIntervalMillis" : 2000,

"heartbeatTimeoutSecs" : 10,

"electionTimeoutMillis" : 10000,

"catchUpTimeoutMillis" : 2000,

"getLastErrorModes" : {

},

"getLastErrorDefaults" : {

"w" : 1,

"wtimeout" : 0

},

"replicaSetId" : ObjectId("5932f142a55dc83eca86ea86")

}

}

hqmongodb:PRIMARY> cfg.members[0].priority = 1

1

hqmongodb:PRIMARY> cfg.members[1].priority = 1

1

hqmongodb:PRIMARY> cfg.members[2].priority = 2      //设置_ID 为 2 的节点为主节点。即当当前主节点发生故障时,该节点就会转变为主节点接管服务

2

hqmongodb:PRIMARY> rs.reconfig(cfg)                 //使配置生效

{ "ok" : 1 }

说明:

MongoDB副本集通过设置priority 决定优先级,默认优先级为1,priority值是0到100之间的数字,数字越大优先级越高,priority=0,则此节点永远不能成为主节点 primay。

cfg.members[0].priority =1 参数,中括号里的数字是执行rs.conf()查看到的节点顺序, 第一个节点是0,第二个节点是 1,第三个节点是 2,以此类推。优先级最高的那个

被设置为主节点。

④分别对两台从节点进行配置

slave-node1节点操作(182.48.115.237)

[root@slave-node1 ~]# ./mongo 182.48.115.237:27017

.....

hqmongodb:SECONDARY> db.getMongo().setSlaveOk()        //设置从节点为只读.注意从节点的前缀现在是SECONDARY。看清楚才设置

slave-node2节点操作(182.48.115.238)

[root@slave-node2 ~]# ./mongo 182.48.115.238:27017

......

hqmongodb:SECONDARY> db.getMongo().setSlaveOk()       //从节点的前缀是SECONDARY,看清楚才设置。执行这个,否则后续从节点同步数据时会报错:"errmsg" : "not master and slaveOk=false"

⑤设置数据库账号,开启登录验证(可跳过,即不开启登陆验证)

5.1)设置数据库账号

在master-node主节点服务器182.48.115.236上面操作

[root@master-node ]# ./mongo 182.48.115.236:27017

......

-------------------------------------------------

如果执行命令后出现报错: "errmsg" : "not master and slaveOk=false",

这是正常的,因为SECONDARY是不允许读写的,如果非要解决,方法如下:

> rs.slaveOk();              //执行这个命令然后,再执行其它命令就不会出现这个报错了

-------------------------------------------------

hqmongodb:PRIMARY> show dbs

local  0.000GB

hqmongodb:PRIMARY> use admin     //mongodb3.0没有admin数据库了,需要手动创建。admin库下添加的账号才是管理员账号

switched to db admin

hqmongodb:PRIMARY> show collections

#添加两个管理员账号,一个系统管理员:system 一个数据库管理员:administrator

#先添加系统管理员账号,用来管理用户

hqmongodb:PRIMARY> db.createUser({user:"system",pwd:"123456",roles:[{role:"root",db:"admin"}]})

Successfully added user: {

"user" : "system",

"roles" : [

{

"role" : "root",

"db" : "admin"

}

]

}

hqmongodb:PRIMARY> db.auth('system','123456')        //添加管理员用户认证,认证之后才能管理所有数据库

1

#添加数据库管理员,用来管理所有数据库

hqmongodb:PRIMARY> db.createUser({user:'administrator', pwd:'123456', roles:[{ role: "userAdminAnyDatabase", db: "admin"}]});

Successfully added user: {

"user" : "administrator",

"roles" : [

{

"role" : "userAdminAnyDatabase",

"db" : "admin"

}

]

}

hqmongodb:PRIMARY> db.auth('administrator','123456')      //添加管理员用户认证,认证之后才能管理所有数据库

1

hqmongodb:PRIMARY> db

admin

hqmongodb:PRIMARY> show collections

system.users

system.version

hqmongodb:PRIMARY> db.system.users.find()

{ "_id" : "admin.system", "user" : "system", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "uTGH9NI6fVUFXd2u7vu3Pw==", "storedKey" : "qJBR7dlqj3IgnWpVbbqBsqo6ECs=", "serverKey" : "pTQhfZohNh760BED7Zn1Vbety4k=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }

{ "_id" : "admin.administrator", "user" : "administrator", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "zJ3IIgYCe4IjZm0twWnK2Q==", "storedKey" : "2UCFc7KK1k5e4BgWbkTKGeuOVB4=", "serverKey" : "eYHK/pBpf8ntrER1A8fiI+GikBY=" } }, "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }

退出,用刚才创建的账号进行登录

[root@master-node ~]# mongo 182.48.115.236:27017 -u system -p 123456 --authenticationDatabase admin

[root@master-node ~]# mongo 182.48.115.236:27017 -u administrator -p 123456  --authenticationDatabase admin

5.2)开启登录验证

在master-node主节点服务器182.48.115.236上面操作

[root@master-node ~]# cd /usr/local/mongodb/                        //切换到mongodb主目录

[root@master-node mongodb]# openssl rand -base64 21 > keyfile       //创建一个 keyfile(使用 openssl 生成 21 位 base64 加密的字符串)

[root@master-node mongodb]# chmod 600 /usr/local/mongodb/keyfile

[root@master-node mongodb]# cat /usr/local/mongodb/keyfile          //查看刚才生成的字符串,做记录,后面要用到

RavtXslz/WTDwwW2JiNvK4OBVKxU

注意:上面的数字 21,最好是 3 的倍数,否则生成的字符串可能含有非法字符,认证失败。

5.3)设置配置文件

分别在所有节点上面操作(即三个节点的配置文件上都要修改)

[root@master-node ~]# vim /usr/local/mongodb/mongodb.conf     //添加下面两行内容

......

auth=true

keyFile =/usr/local/mongodb/keyfile

启动脚本使用下面的代码(注释原来的,启用之前注释掉的)

[root@master-node ~]# cat /etc/init.d/mongodb

#!/bin/sh

# chkconfig: - 64 36

# description:mongod

case $1 in

start)

/usr/local/mongodb/bin/mongod --maxConns 20000 --config /usr/local/mongodb/mongodb.conf

;;

stop)

#/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.shutdownServer()"

/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.auth('system', '123456');db.shutdownServer()"

;;

status)

#/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.stats()"

/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.auth('system', '123456');db.stats()"

;; esac

5.4)设置权限验证文件

先进入master-node主节点服务器182.48.115.236,查看

[root@master-node ~]# cat /usr/local/mongodb/keyfile

RavtXslz/WTDwwW2JiNvK4OBVKxU                              //查看刚才生成的字符串,做记录

再分别进入两台从节点服务器182.48.115.237/238

[root@slave-node1 ~]# vim /usr/local/mongodb/keyfile       //将主节点生成的权限验证字符码复制到从节点的权限验证文件里

RavtXslz/WTDwwW2JiNvK4OBVKxU

[root@slave-node1 ~]# chmod 600 /usr/local/mongodb/keyfile

[root@slave-node2 ~]# vim /usr/local/mongodb/keyfile

[root@slave-node2 ~]# cat /usr/local/mongodb/keyfile

RavtXslz/WTDwwW2JiNvK4OBVKxU

[root@slave-node2 ~]# chmod 600 /usr/local/mongodb/keyfile

⑥验证副本集

分别重启三台副本集服务器(三台节点都要重启)

[root@master-node ~]# ps -ef|grep mongodb

root     28964     1  1 02:22 ?        00:00:31 /usr/local/mongodb/bin/mongod --maxConns 20000 --config /usr/local/mongodb/mongodb.conf

root     29453 28911  0 03:04 pts/0    00:00:00 grep mongodb

[root@master-node ~]# kill -9 28964

[root@master-node ~]# /etc/init.d/mongodb start

about to fork child process, waiting until server is ready for connections.

forked process: 29457

child process started successfully, parent exiting

[root@master-node ~]# lsof -i:27017

COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME

mongod  29457 root    7u  IPv4 701471      0t0  TCP slave-node1:27017 (LISTEN)

mongod  29457 root   29u  IPv4 701526      0t0  TCP slave-node1:27017->master-node:39819 (ESTABLISHED)

mongod  29457 root   30u  IPv4 701573      0t0  TCP slave-node1:27017->master-node:39837 (ESTABLISHED)

mongod  29457 root   31u  IPv4 701530      0t0  TCP slave-node1:36768->master-node:27017 (ESTABLISHED)

mongod  29457 root   32u  IPv4 701549      0t0  TCP slave-node1:36786->master-node:27017 (ESTABLISHED)

mongod  29457 root   33u  IPv4 701574      0t0  TCP slave-node1:27017->master-node:39838 (ESTABLISHED)

然后登陆mongodb

[root@master-node ~]# mongo 182.48.115.236:27017 -u system -p 123456 --authenticationDatabase admin

.......

hqmongodb:PRIMARY> rs.status()             //副本集状态查看.也可以省略上面添加登陆验证的步骤,不做验证,直接查看集群状态。集群状态中可以看出哪个节点目前是主节点

{

"set" : "hqmongodb",

"date" : ISODate("2017-06-03T19:06:59.708Z"),

"myState" : 1,

"term" : NumberLong(2),

"heartbeatIntervalMillis" : NumberLong(2000),

"optimes" : {

"lastCommittedOpTime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"appliedOpTime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"durableOpTime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

}

},

"members" : [

{

"_id" : 0,

"name" : "182.48.115.236:27017",

"health" : 1,

"state" : 1,                         //state的值为1的节点就是主节点

"stateStr" : "PRIMARY",              //主节点PRIMARY标记

"uptime" : 138,

"optime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDate" : ISODate("2017-06-03T19:06:50Z"),

"infoMessage" : "could not find member to sync from",

"electionTime" : Timestamp(1496516709, 1),

"electionDate" : ISODate("2017-06-03T19:05:09Z"),

"configVersion" : 4,

"self" : true

},

{

"_id" : 1,

"name" : "182.48.115.237:27017",

"health" : 1,

"state" : 2,

"stateStr" : "SECONDARY",

"uptime" : 116,

"optime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDurable" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDate" : ISODate("2017-06-03T19:06:50Z"),

"optimeDurableDate" : ISODate("2017-06-03T19:06:50Z"),

"lastHeartbeat" : ISODate("2017-06-03T19:06:59.533Z"),

"lastHeartbeatRecv" : ISODate("2017-06-03T19:06:59.013Z"),

"pingMs" : NumberLong(0),

"syncingTo" : "182.48.115.236:27017",

"configVersion" : 4

},

{

"_id" : 2,

"name" : "182.48.115.238:27017",

"health" : 1,

"state" : 2,

"stateStr" : "SECONDARY",

"uptime" : 189,

"optime" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDurable" : {

"ts" : Timestamp(1496516810, 1),

"t" : NumberLong(2)

},

"optimeDate" : ISODate("2017-06-03T19:06:50Z"),

"optimeDurableDate" : ISODate("2017-06-03T19:06:50Z"),

"lastHeartbeat" : ISODate("2017-06-03T19:06:59.533Z"),

"lastHeartbeatRecv" : ISODate("2017-06-03T19:06:59.013Z"),

"pingMs" : NumberLong(0),

"syncingTo" : "182.48.115.236:27017",

"configVersion" : 4

},

],

"ok" : 1

}

注意上面命令结果中的state,如果这个值为 1,说明是主控节点(master);如果是2,说明是从属节点slave。在上面显示的当前主节点写入数据,到从节点上查看发现数据会同步。

当主节点出现故障的时候,在两个从节点上会选举出一个新的主节点,故障恢复之后,之前的主节点会变为从节点。从上面集群状态中开看出,当前主节点是master-node,那么关闭它的mongodb,再次查看集群状态,就会发现主节点变为之前设置的slave-node2,即182.48.115.238了!

至此,Linux 下 MongoDB 副本集部署完成。

⑦测试

1)主从服务器数据是否同步,从服务器没有读写权限

a:向主服务器写入数据 ok 后台自动同步到从服务器,从服务器有数据

b:向从服务器写入数据 false 从服务器不能写

c:主服务器读取数据 ok

d:从服务器读取数据 false 从服务器不能读

2)关闭主服务器,从服务器是否能顶替

mongo 的命令行执行 rs.status() 发现 PRIMARY 替换了主机了

3)关闭的服务器,再恢复,以及主从切换

a:直接启动关闭的服务,rs.status()中会发现,原来挂掉的主服务器重启后变成从服务器了

b:额外删除新的服务器 rs.remove("localhost:9933"); rs.status()

c:额外增加新的服务器 rs.add({_id:0,host:"localhost:9933",priority:1});

d:让新增的成为主服务器 rs.stepDown(),注意之前的 priority 投票

4)从服务器读写

db.getMongo().setSlaveOk();

db.getMongo().slaveOk();//从库只读,没有写权限,这个方法 java 里面不推荐了

db.setReadPreference(ReadPreference.secondaryPreferred());// 在 复 制 集 中 优 先 读secondary,如果 secondary 访问不了的时候就从 master 中读

db.setReadPreference(ReadPreference.secondary());// 只 从 secondary 中 读 , 如 果secondary 访问不了的时候就不能进行查询

⑧日志查看

MongoDB 的 Replica Set 架构是通过一个日志来存储写操作的,这个日志就叫做”oplog”,它存在于”local”数据库中,oplog 的大小是可以通过 mongod 的参数”—oplogSize”来改变oplog 的日志大小。

> use local

switched to db local

> db.oplog.rs.find()

{ "ts" : { "t" : 1342511269000, "i" : 1 }, "h" : NumberLong(0), "op" : "n", "ns" : "", "o" :

{ "msg" : "initiating set" } }

字段说明:

ts: 某个操作的时间戳

op: 操作类型,如下:

i: insert

d: delete

u: update

ns: 命名空间,也就是操作的 collection name

⑨其它注意细节

rs.remove("ip:port");          //删除副本

单服务器,启动时添加--auth         参数开启验证。

副本集服务器,开启--auth 参数的同时,必须指定 keyfile 参数,节点之间的通讯基于该 keyfile,key 长度必须在 6 到 1024 个字符之间,最好为 3 的倍数,不能含有非法字符。

重新设置副本集

rs.stepDown()

cfg = rs.conf()

cfg.members[n].host= 'new_host_name:prot'

rs.reconfig(cfg)

副本集所有节点服务器总数必须为奇数,服务器数量为偶数的时候,需要添加一个仲裁节 点,仲裁节点不参与数副本集,只有选举权。

rs.addArb("182.48.115.239:27017") #添加仲裁节点