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
[root@master-node ~]# ./mongod --config=mongodb.conf
[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") #添加仲裁节点