1、解压并设置可执行文件
#tar xf mongodb-linux-x86_64-rhel62-3.4.2.tgz
#mv mongodb-linux-x86_64-rhel62-3.4.2 /usr/local/mongodb #将解压的包拷贝到指定目录
MongoDB 的可执行文件位于bin目录下,所以可以将其添加到 PATH 路径中:
#vi /etc/profile
export
PATH=$PATH:
/usr/local/mongodb/bin
加载
/etc/profile
使定义生效
#. /etc/profile
查看mongodb的bin文件
#which mongo
/usr/local/mongodb/bin/mongo
查看mongodb的版本号
#mongo --version
MongoDB shell version v3.4.2
git version: 3f76e40c105fc223b3e5aac3e20dcd026b83b38b
OpenSSL version: OpenSSL 1.0.1e-fips 11 Feb 2013
allocator: tcmalloc
modules: none
build environment:
distmod: rhel62
distarch: x86_64
target_arch: x86_64
2、创建数据库目录
MongoDB的数据存储在data目录的db目录下,但是这个目录在源码安装过程不会自动创建,所以需要
手动创建data目录,并在data目录中创建db目录。以下实例中我们将data目录创建于根目录下(/)。
注意:
/data/db
是MongoDB默认的启动的数据库路径(--dbpath)。也可以单独使用一块盘挂载到
/data/db
目录,这样数据就单独存放了,一定程度提高了安全性!
#mkdir /data/db -p
3、启动mongodb
注意:如果你的数据库目录不是
/data/db
,可以通过 --dbpath 来指定
#mongod
2017-02-14T11:28:58.380+0800 I CONTROL [initandlisten] MongoDB starting : pid=9803 port=27017 dbpath=
/data/db
64-bit host=template
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] db version v3.4.2
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] git version: 3f76e40c105fc223b3e5aac3e20dcd026b83b38b
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] OpenSSL version: OpenSSL 1.0.1e-fips 11 Feb 2013
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] allocator: tcmalloc
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] modules: none
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] build environment:
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] distmod: rhel62
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] distarch: x86_64
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] target_arch: x86_64
2017-02-14T11:28:58.381+0800 I CONTROL [initandlisten] options: {}
2017-02-14T11:28:58.432+0800 I STORAGE [initandlisten]
2017-02-14T11:28:58.432+0800 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2017-02-14T11:28:58.432+0800 I STORAGE [initandlisten] ** See http:
//dochub
.mongodb.org
/core/prodnotes-filesystem
2017-02-14T11:28:58.432+0800 I STORAGE [initandlisten] wiredtiger_open config: create,cache_size=426M,session_max=20000,eviction=(threads_max=4),config_base=
false
,statistics=(fast),log=(enabled=
true
,archive=
true
,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),checkpoint=(wait=60,log_size=2GB),statistics_log=(wait=0),
2017-02-14T11:28:58.494+0800 I CONTROL [initandlisten]
2017-02-14T11:28:58.494+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled
for
the database.
2017-02-14T11:28:58.494+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2017-02-14T11:28:58.494+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user,
which
is not recommended.
2017-02-14T11:28:58.494+0800 I CONTROL [initandlisten]
2017-02-14T11:28:58.494+0800 I CONTROL [initandlisten]
2017-02-14T11:28:58.494+0800 I CONTROL [initandlisten] ** WARNING:
/sys/kernel/mm/transparent_hugepage/enabled
is
'always'
.
2017-02-14T11:28:58.494+0800 I CONTROL [initandlisten] ** We suggest setting it to
'never'
2017-02-14T11:28:58.495+0800 I CONTROL [initandlisten]
2017-02-14T11:28:58.495+0800 I CONTROL [initandlisten] ** WARNING:
/sys/kernel/mm/transparent_hugepage/defrag
is
'always'
.
2017-02-14T11:28:58.495+0800 I CONTROL [initandlisten] ** We suggest setting it to
'never'
2017-02-14T11:28:58.495+0800 I CONTROL [initandlisten]
2017-02-14T11:28:58.495+0800 I CONTROL [initandlisten] ** WARNING: soft rlimits too low. rlimits
set
to 14867 processes, 65536 files. Number of processes should be at least 32768 : 0.5
times
number of files.
2017-02-14T11:28:58.495+0800 I CONTROL [initandlisten]
2017-02-14T11:28:58.501+0800 I FTDC [initandlisten] Initializing full-
time
diagnostic data capture with directory
'/data/db/diagnostic.data'
2017-02-14T11:28:58.511+0800 I INDEX [initandlisten] build index on: admin.system.version properties: {
v
: 2, key: { version: 1 }, name:
"incompatible_with_version_32"
, ns:
"admin.system.version"
}
2017-02-14T11:28:58.511+0800 I INDEX [initandlisten] building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2017-02-14T11:28:58.512+0800 I INDEX [initandlisten] build index
done
. scanned 0 total records. 0 secs
2017-02-14T11:28:58.513+0800 I COMMAND [initandlisten] setting featureCompatibilityVersion to 3.4
2017-02-14T11:28:58.513+0800 I NETWORK [thread1] waiting
for
connections on port 27017
根据提示设置
ulimit
和
/sys/kernel
参数:
#echo never > /sys/kernel/mm/transparent_hugepage/enabled
#echo never> /sys/kernel/mm/transparent_hugepage/defrag
#vi /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
* soft nproc 32768
* hard nproc 32768
注:设置好之后,不会立即生效,需要
exit
退出shell,然后重新连接生效。重新启动mongod进程,警告
没有那么多了,但是还会提示如下几个:
** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
** WARNING: Access control is not enabled
for
the database.
** WARNING: You are running this process as the root user,
which
is not recommended
其中第一个和第二个比较容易解决,在配置文件添加参数就ok,第三个是提示不允许使用root用户启动
mongod进程,上一篇yum安装的没有遇到类似问题,是因为yum安装之后,利用启动脚本启动的mongod进
是普通用户。
4、以普通用户身份启动mongod
创建一个普通用户mongod
#useradd -r -m -s /bin/bash mongod #创建一个普通系统用户
修改权限
#chown mongod.mongod /data/ -R
创建mongodb日志目录并修改权限
#mkdir /var/log/mongodb/
#chown mongod.mongod /var/log/mongodb
创建配置文件mongod.conf
#vi /etc/mongod.conf
# mongod.conf
# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/
# where to write logging data.
systemLog:
destination:
file
logAppend:
true
path:
/var/log/mongodb/mongod
.log
logRotate: rename
# Where and how to store data.
storage:
dbPath:
/data/db
journal:
enabled:
true
engine: wiredTiger
# mmapv1:
# wiredTiger:
# how the process runs
processManagement:
fork:
true
# fork and run in background
pidFilePath:
/usr/local/mongodb/mongod
.pid
# location of pidfile
# network interfaces
net:
port: 27017
bindIp: 10.0.18.149
# Listen to local interface only, comment to listen on all interfaces.
http:
enabled:
false
RESTInterfaceEnabled:
false
#setParameter:
# enableLocalhostAuthBypass: true
security:
authorization: enabled
#operationProfiling:
#replication:
#sharding:
## Enterprise-Only Options
#auditLog:
#snmp:
保存退出,以上配置文件开启了authorization功能!
启动命令:
#su mongod -c "/usr/local/mongodb/bin/mongod -f /etc/mongod.conf"
about to fork child process, waiting
until
server is ready
for
connections.
forked process: 11274
child process started successfully, parent exiting
查看端口
#netstat -tunlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID
/Program
name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1026
/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1103
/master
tcp 0 0 10.0.18.149:27017 0.0.0.0:* LISTEN 11274
/mongod
tcp 0 0 :::22 :::* LISTEN 1026
/sshd
tcp 0 0 ::1:25 :::* LISTEN 1103
/master
查看进程是否是mongod启动的
#ps aux | grep mongod
mongod 11274 0.8 1.8 341288 35872 ? Sl 16:03 0:01
/usr/local/mongodb/bin/mongod
-f
/etc/mongod
.conf
root 11302 0.0 0.0 103244 836 pts
/1
S+ 16:06 0:00
grep
mongod
注:如果想在启动的时候开启认证,使用命令:mongod --dbpath
/data/db
--port 20000 --auth
如果配置以普通用户启动mongod进程,也需要修改
/etc/security/limit
.d
/90-nproc
.conf
#vi /etc/security/limits.d/90-nproc.conf ##添加如下2行
* soft nproc 32768
* hard nproc 32768
注意:可以用killall mongod 来停止mongodb服务;但是不能用
kill
-9 来停止,会损坏服务器;还可
以用
ps
aux|
grep
mongod命令来查看PID,再用
kill
-2 PID停止服务
二、配置MongoDB的复制(主从复制)
1、什么是复制
MongoDB复制是将数据同步在多个服务器的过程。复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。复制还允许您从硬件故障和服务中断中恢复数据。
Mongodb复制集由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点,Mongodb Driver(客户端)的所有数据都写入Primary,Secondary从Primary同步写入的数据,以保持复制集内所有成员存储相同的数据集,提供数据的高可用。
2、配置复制的优点
保障数据的安全性
数据高可用性 (24*7)
灾难恢复
无需停机维护(如备份,重建索引,压缩)
分布式读取数据
3、MongoDB复制原理
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。mongodb各个节点常见的搭配方式为:一主一从、一主多从。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致,如下图:
大致流程是:客户端从主节点读取数据,在客户端写入数据到主节点时,主节点与从节点进行数据交互保障数据的一致性。
4、配置MongoDB的主从复制(不常用)
主IP:10.0.18.144 从IP:10.0.18.145
注:复制集已经在大多数场合下替代了master-slave复制。如果可能的话,尽可能使用复制集而不是主-从复制架构。本文旨在于为之前遗留的主从架构提供支持,或是仅用于学习。
补充:配置主从,暂时将配置文件中的认证功能关闭-->注释掉security.authorization: enabled
在主服务器关闭mongod进程,重新启动如下:
#killall mongod #关闭mongod进程
然后重新启动:
#su mongod -c "/usr/local/mongodb/bin/mongod --master -f /etc/mongod.conf"
about to fork child process, waiting
until
server is ready
for
connections.
forked process: 10430
child process started successfully, parent exiting
在从服务器关闭mongod进程,重新启动如下:
#su mongod -c "/usr/local/mongodb/bin/mongod --slave --source 10.0.18.144:27017 -f /etc/mongod.conf"
about to fork child process, waiting
until
server is ready
for
connections.
forked process: 14206
child process started successfully, parent exiting
这样mongodb的主从就配置ok了。
a、在master服务器上操作
首先看下日志
#tail /var/log/mongodb/mongod.log
………………
2017-02-15T17:02:16.309+0800 I STORAGE [initandlisten] Scanning the oplog to determine where to place markers
for
truncation
2017-02-15T17:02:16.322+0800 I REPL [initandlisten] ******
2017-02-15T17:02:16.325+0800 I NETWORK [thread1] waiting
for
connections on port 27017
2017-02-15T17:04:09.031+0800 I NETWORK [thread1] connection accepted from 10.0.18.145:46767
#1 (1 connection now open)
2017-02-15T17:04:09.032+0800 I NETWORK [conn1] received client metadata from 10.0.18.145:46767 conn1: { driver: { name:
"MongoDB Internal Client"
, version:
"3.4.2"
}, os: {
type
:
"Linux"
, name:
"CentOS release 6.6 (Final)"
, architecture:
"x86_64"
, version:
"Kernel 2.6.32-504.el6.x86_64"
} }
2017-02-15T17:04:09.045+0800 I - [conn1] end connection 10.0.18.145:46767 (1 connection now
open
)
2017-02-15T17:04:12.033+0800 I NETWORK [thread1] connection accepted from 10.0.18.145:46828
#2 (1 connection now open)
2017-02-15T17:04:12.034+0800 I NETWORK [conn2] received client metadata from 10.0.18.145:46828 conn2: { driver: { name:
"MongoDB Internal Client"
, version:
"3.4.2"
}, os: {
type
:
"Linux"
, name:
"CentOS release 6.6 (Final)"
, architecture:
"x86_64"
, version:
"Kernel 2.6.32-504.el6.x86_64"
} }
2017-02-15T17:04:12.035+0800 I - [conn2] end connection 10.0.18.145:46828 (1 connection now
open
)
2017-02-15T17:04:15.036+0800 I NETWORK [thread1] connection accepted from 10.0.18.145:46887
#3 (1 connection now open)
2017-02-15T17:04:15.036+0800 I NETWORK [conn3] received client metadata from 10.0.18.145:46887 conn3: { driver: { name:
"MongoDB Internal Client"
, version:
"3.4.2"
}, os: {
type
:
"Linux"
, name:
"CentOS release 6.6 (Final)"
, architecture:
"x86_64"
, version:
"Kernel 2.6.32-504.el6.x86_64"
} }
可以看到已经接受了从服务器10.0.18.145的连接请求。
进入mongodb进行查看
#mongo --host 10.0.18.144
> db.isMaster()
{
"ismaster"
:
true
,
#表示是主节点
"maxBsonObjectSize"
: 16777216,
"maxMessageSizeBytes"
: 48000000,
"maxWriteBatchSize"
: 1000,
"localTime"
: ISODate(
"2017-02-15T09:08:01.081Z"
),
"maxWireVersion"
: 5,
"minWireVersion"
: 0,
"readOnly"
:
false
,
"ok"
: 1
}
在主服务器创建一个库,并插入数据,验证从服务器是否存在
> show dbs;
admin 0.000GB
local
0.000GB
> db.
test
test
.
test
> db
test
> db.
test
.insert({
"name"
:
"ceshizhucong"
})
WriteResult({
"nInserted"
: 1 })
> show dbs;
admin 0.000GB
local
0.000GB
test
0.000GB
> use
test
switched to db
test
> db.
test
.findOne();db.
test
.findOne();
{
"_id"
: ObjectId(
"58a41cbda6bb67ceb5dd53c6"
),
"name"
:
"ceshizhucong"
}
b、在从服务器操作
#mongo --host 10.0.18.145
> db.isMaster();
{
"ismaster"
:
false
,
#表示是从节点
"maxBsonObjectSize"
: 16777216,
"maxMessageSizeBytes"
: 48000000,
"maxWriteBatchSize"
: 1000,
"localTime"
: ISODate(
"2017-02-15T09:19:30.163Z"
),
"maxWireVersion"
: 5,
"minWireVersion"
: 0,
"readOnly"
:
false
,
"ok"
: 1
}
> show dbs;
2017-02-15T17:23:35.795+0800 E QUERY [thread1] Error: listDatabases failed:{
"ok"
: 0,
"errmsg"
:
"not master and slaveOk=false"
,
"code"
: 13435,
"codeName"
:
"NotMasterNoSlaveOk"
} :
_getErrorWithCode@src
/mongo/shell/utils
.js:25:13
Mongo.prototype.getDBs@src
/mongo/shell/mongo
.js:62:1
shellHelper.show@src
/mongo/shell/utils
.js:755:19
shellHelper@src
/mongo/shell/utils
.js:645:15
@(shellhelp2):1:1
可以看到执行show dbs命令不成功,因为不是master服务器
> db
#查看当前数据库,可以看到test库已经复制过来了
test
> use web
switched to db web
> db
web
> db.web.insert({
"name"
:
"ceshizhucong3"
})db.web.insert({
"name"
:
"ceshizhucong3"
})
WriteResult({
"writeError"
: {
"code"
: 10107,
"errmsg"
:
"not master"
} })
也无法插入数据,因为从服务器无法进行写操作!
查看从服务器日志
#tail -f mongod.log
………………
2017-02-15T17:29:13.730+0800 I REPL [replslave] syncing from host:10.0.18.144:27017
2017-02-15T17:29:14.732+0800 I REPL [replslave] syncing from host:10.0.18.144:27017
到这里MongoDB的主从复制就结束了!
三、MongoDB副本集的一些特征介绍
1、副本集特征:
N个节点的集群
任何节点可作为主节点
所有写入操作都在主节点上
自动故障转移
自动恢复
2、Primary选举
复制集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,
并发起Priamry选举操作,获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary
3、大多数的定义
假设复制集内投票成员(后续介绍)数量为N,则大多数为N/2 + 1,当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。如下图:
通常建议将复制集成员数量设置为奇数,从上表可以看出3个节点和4个节点的复制集都只能容忍1个节点失效,从『服务可用性』的角度看,其效果是一样的(但无疑4个节点能提供更可靠的数据存储)
4、特殊的Secondary
正常情况下,复制集的Seconary会参与Primary选举(自身也可能会被选为Primary),并从Primary同步最新写入的数据,以保证与Primary存储相同的数据。Secondary可以提供读服务,增加Secondary节点可以提供复制集的读服务能力,同时提升复制集的可用性。另外,Mongodb支持对复制集的Secondary节点进行灵活的配置,以适应多种场景的需求
5、Arbiter 仲裁节点
Arbiter节点只参与投票,不能被选为Primary,并且不从Primary同步数据。比如你部署了一个2个节点的复制集,1个Primary,1个Secondary,任意节点宕机,复制集将不能提供服务了(无法选出Primary),这时可以给复制集添加一个Arbiter节点,即使有节点宕机,仍能选出Primary。
Arbiter本身不存储数据,是非常轻量级的服务,当复制集成员为偶数时,最好加入一个Arbiter节点,以提升复制集可用性。
6、Priority0
Priority0节点的选举优先级为0,不会被选举为Primary。比如你跨机房A、B部署了一个复制集,并且想指定Primary必须在A机房,这时可以将B机房的复制集成员Priority设置为0,这样Primary就一定会是A机房的成员。
(注意:如果这样部署,最好将『大多数』节点部署在A机房,否则网络分区时可能无法选出Primary)
7、Vote0
Mongodb 3.0里,复制集成员最多50个,参与Primary选举投票的成员最多7个,其他成员(Vote0)的vote属性必须设置为0,即不参与投票。
8、Hidden
Hidden节点不能被选为主(Priority为0),并且对Driver不可见。
因Hidden节点不会接受Driver的请求,可使用Hidden节点做一些数据备份、离线计算的任务,不会影响复制集的服务。
9、Delayed
Delayed节点必须是Hidden节点,并且其数据落后与Primary一段时间(可配置,比如1个小时)。
因Delayed节点的数据比Primary落后一段时间,当错误或者无效的数据写入Primary时,可通过Delayed节点的数据来恢复到之前的时间点。
10、数据同步
Primary与Secondary之间通过oplog来同步数据,Primary上的写操作完成后,会向特殊的local.oplog.rs特殊集合写入一条oplog,Secondary不断的从Primary取新的oplog并应用。因oplog的数据会不断增加,local.oplog.rs被设置成为一个capped集合,当容量达到配置上限时,会将最旧的数据删除掉。另外考虑到oplog在Secondary上可能重复应用,oplog必须具有幂等性,即重复应用也会得到相同的结果。
Secondary初次同步数据时,会先进行init sync 从Primary(或其他数据更新的Secondary)同步全量数据,然后不断通过tailable cursor从Primary的local.oplog.rs集合里查询最新的oplog并应用到自身。
init sync过程包含如下步骤:
T1时间,从Primary同步所有数据库的数据(local除外),通过listDatabases + listCollections + cloneCollection敏命令组合完成,假设T2时间完成所有操作。
从Primary应用[T1-T2]时间段内的所有oplog,可能部分操作已经包含在步骤1,但由于oplog的幂等性,可重复应用。
根据Primary各集合的index设置,在Secondary上为相应集合创建index。(每个集合_id的index已在步骤1中完成)。
oplog集合的大小应根据DB规模及应用写入需求合理配置,配置得太大,会造成存储空间的浪费;配置得太小,可能造成Secondary的init sync一直无法成功。比如在步骤1里由于DB数据太多、并且oplog配置太小,导致oplog不足以存储[T1, T2]时间内的所有oplog,这就让Secondary无法从Primary上同步完整的数据集。
11、修改复制集配置
当需要修改复制集时,比如增加成员、删除成员、或者修改成员配置(如priorty、vote、hidden、delayed等属性),可通过replSetReconfig命令(rs.reconfig())对复制集进行重新配置。
比如将复制集的第2个成员Priority设置为2,可执行如下命令
cfg = rs.conf();
cfg.members[1].priority = 2;
rs.reconfig(cfg);
细说Primary选举
Primary选举除了在复制集初始化时发生,还有如下场景
复制集被reconfig
Secondary节点检测到Primary宕机时,会触发新Primary的选举
当有Primary节点主动stepDown(主动降级为Secondary)时,也会触发新的Primary选举
Primary的选举受节点间心跳、优先级、最新的oplog时间等多种因素影响。
12、节点间心跳
复制集成员间默认每2s会发送一次心跳信息,如果10s未收到某个节点的心跳,则认为该节点已宕机;如果宕机的节点为Primary,Secondary(前提是可被选为Primary)会发起新的Primary选举。
节点优先级
每个节点都倾向于投票给优先级最高的节点
优先级为0的节点不会主动发起Primary选举
当Primary发现有优先级更高Secondary,并且该Secondary的数据落后在10s内,则Primary会主动降级,让优先级更高的Secondary有成为Primary的机会。
13、Optime
拥有最新optime(最近一条oplog的时间戳)的节点才能被选为主。
14、网络分区
只有更大多数投票节点间保持网络连通,才有机会被选Primary;如果Primary与大多数的节点断开连接,Primary会主动降级为Secondary。
当发生网络分区时,可能在短时间内出现多个Primary,故Driver在写入时,最好设置『大多数成功』的策略,这样即使出现多个Primary,也只有一个Primary能成功写入大多数。
15、复制集的读写设置(Read Preference)
默认情况下,复制集的所有读请求都发到Primary,Driver可通过设置Read Preference来将读请求路由到其他的节点。
primary: 默认规则,所有读请求发到Primary
primaryPreferred: Primary优先,如果Primary不可达,请求Secondary
secondary: 所有的读请求都发到secondary
secondaryPreferred:Secondary优先,当所有Secondary不可达时,请求Primary
nearest:读请求发送到最近的可达节点上(通过ping探测得出最近的节点)
16、Write Concern
默认情况下,Primary完成写操作即返回,Driver可通过设置[Write Concern(https://docs.mongodb.org/manual/core/write-concern/)来设置写成功的规则。
如下的write concern规则设置必须在大多数节点上成功,超时时间为5s。
db.products.insert(
{ item: "envelopes", qty : 100, type: "Clasp" },
{ writeConcern: { w: majority, wtimeout: 5000 } }
)
17、异常处理(rollback)
当Primary宕机时,如果有数据未同步到Secondary,当Primary重新加入时,如果新的Primary上已经发生了写操作,则旧Primary需要回滚部分操作,以保证数据集与新的Primary一致。
旧Primary将回滚的数据写到单独的rollback目录下,数据库管理员可根据需要使用mongorestore进行恢复。
四、开始配置MongoDB副本集:
注:这里使用3台服务器做MongoDB的副本集,都是源码安装的3.4.2版本,架构信息如下:
主:10.0.18.144 centos6.6 x86_64
从:10.0.18.149 centos6.6 x86_64
从:10.0.18.150 centos6.6 x86_64
具体安装步骤请参见本篇博客第一部分
1、修改配置文件
基本配置如下:
systemLog:
destination:
file
logAppend:
true
path:
/var/log/mongodb/mongod
.log
logRotate: rename
storage:
dbPath:
/data/db
journal:
enabled:
true
engine: wiredTiger
processManagement:
fork:
true
# fork and run in background
pidFilePath:
/usr/local/mongodb/mongod
.pid
# location of pidfile
net:
port: 27017
bindIp: x.x.x.x
# Listen to local interface only, comment to listen on all interfaces.
http:
enabled:
false
RESTInterfaceEnabled:
false
#security:
# authorization: enabled
在基本配置的基础上对三台mongodb的配置文件统一添加如下:
replication:
oplogSizeMB: 20
replSetName: Myrepl
注:oplogSizeMB指oplog大小,即复制操作日志的最大大小(以兆字节为单位
replSetName 指副本集名称,注意格式是前面空两格,冒号后空一格。
然后重启三台MongoDB的mongod进程!
#killall mongod
#su mongod -c "/usr/local/mongodb/bin/mongod -f /etc/mongod.conf" #以普通用户启动
2、在主服务器操作
#mongo --host 10.0.18.144
MongoDB shell version v3.4.2
connecting to: mongodb:
//10
.0.18.144:27017/
MongoDB server version: 3.4.2
> use admin
switched to db admin
> config={_id:
"Myrepl"
,members:[{_id:0,host:
"10.0.18.144:27017"
},{_id:1,host:
"10.0.18.149:27017"
},{_id:2,host:
"10.0.18.150:27017"
}]}config={_id:
"Myrepl"
,members:[{_id:0,host:
"10.0.18.144:27017"
},{_id:1,host:
"10.0.18.149:27017"
},{_id:2,host:
"10.0.18.150:27017"
}]}
{
"_id"
:
"Myrepl"
,
"members"
: [
{
"_id"
: 0,
"host"
:
"10.0.18.144:27017"
},
{
"_id"
: 1,
"host"
:
"10.0.18.149:27017"
},
{
"_id"
: 2,
"host"
:
"10.0.18.150:27017"
}
]
}
> rs.initiate(config)
#初始化
{
"ok"
: 1 }
#显示ok表示成功
Myrepl:OTHER>
补充:要想使用副本集,从的mongodb的数据必须都是空的,要不然执行rs.initiate()命令时会提示因
存在数据而导致初始化不成功,如下:
> rs.initiate(config)
{
"ok"
: 0,
"errmsg"
:
"'10.0.18.150:27017' has data already, cannot initiate set."
,
"code"
: 110,
"codeName"
:
"CannotInitializeNodeWithData"
}
继续进行:
Myrepl:OTHER> rs.status()
#查看副本集状态
{
"set"
:
"Myrepl"
,
"date"
: ISODate(
"2017-02-16T08:57:54.003Z"
),
"myState"
: 1,
"term"
: NumberLong(1),
"heartbeatIntervalMillis"
: NumberLong(2000),
"optimes"
: {
"lastCommittedOpTime"
: {
"ts"
: Timestamp(1487235464, 1),
"t"
: NumberLong(1)
},
"appliedOpTime"
: {
"ts"
: Timestamp(1487235464, 1),
"t"
: NumberLong(1)
},
"durableOpTime"
: {
"ts"
: Timestamp(1487235464, 1),
"t"
: NumberLong(1)
}
},
"members"
: [
{
"_id"
: 0,
"name"
:
"10.0.18.144:27017"
,
"health"
: 1,
"state"
: 1,
"stateStr"
:
"PRIMARY"
,
#主
"uptime"
: 941,
"optime"
: {
"ts"
: Timestamp(1487235464, 1),
"t"
: NumberLong(1)
},
"optimeDate"
: ISODate(
"2017-02-16T08:57:44Z"
),
"electionTime"
: Timestamp(1487235294, 1),
"electionDate"
: ISODate(
"2017-02-16T08:54:54Z"
),
"configVersion"
: 1,
"self"
:
true
},
{
"_id"
: 1,
"name"
:
"10.0.18.149:27017"
,
"health"
: 1,
"state"
: 2,
"stateStr"
:
"SECONDARY"
,
#从
"uptime"
: 191,
"optime"
: {
"ts"
: Timestamp(1487235464, 1),
"t"
: NumberLong(1)
},
"optimeDurable"
: {
"ts"
: Timestamp(1487235464, 1),
"t"
: NumberLong(1)
},
"optimeDate"
: ISODate(
"2017-02-16T08:57:44Z"
),
"optimeDurableDate"
: ISODate(
"2017-02-16T08:57:44Z"
),
"lastHeartbeat"
: ISODate(
"2017-02-16T08:57:52.541Z"
),
"lastHeartbeatRecv"
: ISODate(
"2017-02-16T08:57:52.182Z"
),
"pingMs"
: NumberLong(0),
"syncingTo"
:
"10.0.18.150:27017"
,
"configVersion"
: 1
},
{
"_id"
: 2,
"name"
:
"10.0.18.150:27017"
,
"health"
: 1,
"state"
: 2,
"stateStr"
:
"SECONDARY"
,
#从
"uptime"
: 191,
"optime"
: {
"ts"
: Timestamp(1487235464, 1),
"t"
: NumberLong(1)
},
"optimeDurable"
: {
"ts"
: Timestamp(1487235464, 1),
"t"
: NumberLong(1)
},
"optimeDate"
: ISODate(
"2017-02-16T08:57:44Z"
),
"optimeDurableDate"
: ISODate(
"2017-02-16T08:57:44Z"
),
"lastHeartbeat"
: ISODate(
"2017-02-16T08:57:52.542Z"
),
"lastHeartbeatRecv"
: ISODate(
"2017-02-16T08:57:52.092Z"
),
"pingMs"
: NumberLong(0),
"syncingTo"
:
"10.0.18.144:27017"
,
"configVersion"
: 1
}
],
"ok"
: 1
}
注意:两个从上的状态为
"stateStr"
:
"SECONDARY"
,若为
"STARTUP"
,则需要进行如下操作:
> var config={_id:
"tpp"
,members:[{_id:0,host:
"192.168.0.103:27017"
},{_id:1,host:
"192.168.0.109:27017"
},{_id:2,host:
"192.168.0.120:27017"
}]}
> rs.initiate(config)
查看到以上信息就说明配置成功了,我们也会发现主上前缀变为Myrepl:PRIMARY> 而从服务器的前缀
变成了Myrepl:SECONDARY>
3、副本集测试
在主服务器上建库、建集合、插入数据
Myrepl:PRIMARY> use testdb
switched to db testdb
Myrepl:PRIMARY> db.createCollection(
'test1'
)
{
"ok"
: 1 }
Myrepl:PRIMARY> show dbs
admin 0.000GB
local
0.000GB
testdb 0.000GB
Myrepl:PRIMARY> db.testdb.insert({
"name"
:
"repl"
,
"time"
:
"170216"
})
#插入一条数据
WriteResult({
"nInserted"
: 1 })
Myrepl:PRIMARY> db.testdb.findOne()
#查看数据
{
"_id"
: ObjectId(
"58a56b7404b33bc093a90321"
),
"name"
:
"repl"
,
"time"
:
"170216"
}
然后再从服务器上查看
#mongo --host 10.0.18.149
MongoDB shell version v3.4.2
connecting to: mongodb:
//10
.0.18.149:27017/
MongoDB server version: 3.4.2
Myrepl:SECONDARY> show dbs
2017-02-16T17:08:09.524+0800 E QUERY [thread1] Error: listDatabases failed:{
"ok"
: 0,
"errmsg"
:
"not master and slaveOk=false"
,
"code"
: 13435,
"codeName"
:
"NotMasterNoSlaveOk"
} :
_getErrorWithCode@src
/mongo/shell/utils
.js:25:13
Mongo.prototype.getDBs@src
/mongo/shell/mongo
.js:62:1
shellHelper.show@src
/mongo/shell/utils
.js:755:19
shellHelper@src
/mongo/shell/utils
.js:645:15
@(shellhelp2):1:1
Myrepl:SECONDARY> rs.slaveOk()
#执行此命令,之后查询就不报错了!
Myrepl:SECONDARY> show dbs
admin 0.000GB
local
0.000GB
testdb 0.000GB
Myrepl:SECONDARY> use testd
switched to db testdb
Myrepl:SECONDARY> db.testdb.findOne()
#查询数据,可以看到在从服务器上已经有数据了
{
"_id"
: ObjectId(
"58a56b7404b33bc093a90321"
),
"name"
:
"repl"
,
"time"
:
"170216"
}
注意:通过上面的命令只能临时查询,下次再通过mongo命令进入后查询仍会报错,所以可以修改文件
#vi ~/.mongorc.js //写入下面这行
rs.slaveOk();
然后重启mongod进程!
我这里是root用户,所以在
/root/
.mongorc.js中添加即可!
4、模拟主服务器宕机,查看从服务器转变为主
首先在主服务器查看三台服务器的权重,如下:
Myrepl:PRIMARY> rs.config()
{
"_id"
:
"Myrepl"
,
"version"
: 1,
"protocolVersion"
: NumberLong(1),
"members"
: [
{
"_id"
: 0,
"host"
:
"10.0.18.144:27017"
,
"arbiterOnly"
:
false
,
"buildIndexes"
:
true
,
"hidden"
:
false
,
"priority"
: 1,
#优先级为1
"tags"
: {
},
"slaveDelay"
: NumberLong(0),
"votes"
: 1
},
{
"_id"
: 1,
"host"
:
"10.0.18.149:27017"
,
"arbiterOnly"
:
false
,
"buildIndexes"
:
true
,
"hidden"
:
false
,
"priority"
: 1,
#优先级为1
"tags"
: {
},
"slaveDelay"
: NumberLong(0),
"votes"
: 1
},
{
"_id"
: 2,
"host"
:
"10.0.18.150:27017"
,
"arbiterOnly"
:
false
,
"buildIndexes"
:
true
,
"hidden"
:
false
,
"priority"
: 1,
#优先级为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(
"58a568d2b205e55e0e0682d1"
)
}
}
注意:默认所有的机器权重(优先级)都为1,如果任何一个权重设置为比其他的高,则该台机器立马会切换为primary角色,所以要我们预设三台机器的权重
设置权重
预设主(18.144)权重为3、从(18.149)权重为2、从(18.150)权重为1(默认值可不用重新赋值)。
在主18.144上执行下面命令:
Myrepl:PRIMARY> cfg=rs.config()
#重新赋值
{
"_id"
:
"Myrepl"
,
"version"
: 1,
"protocolVersion"
: NumberLong(1),
"members"
: [
{
"_id"
: 0,
"host"
:
"10.0.18.144:27017"
,
"arbiterOnly"
:
false
,
"buildIndexes"
:
true
,
"hidden"
:
false
,
"priority"
: 1,
"tags"
: {
},
"slaveDelay"
: NumberLong(0),
"votes"
: 1
},
{
"_id"
: 1,
"host"
:
"10.0.18.149:27017"
,
"arbiterOnly"
:
false
,
"buildIndexes"
:
true
,
"hidden"
:
false
,
"priority"
: 1,
"tags"
: {
},
"slaveDelay"
: NumberLong(0),
"votes"
: 1
},
{
"_id"
: 2,
"host"
:
"10.0.18.150: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(
"58a568d2b205e55e0e0682d1"
)
}
}
Myrepl:PRIMARY> cfg.members[0].priority = 3
3
Myrepl:PRIMARY> cfg.members[1].priority = 2
2
Myrepl:PRIMARY> rs.reconfig(cfg)
#重新加载配置
{
"ok"
: 1 }
Myrepl:PRIMARY> rs.config()
#重新查看权重是否已经更改
{
"_id"
:
"Myrepl"
,
"version"
: 2,
"protocolVersion"
: NumberLong(1),
"members"
: [
{
"_id"
: 0,
"host"
:
"10.0.18.144:27017"
,
"arbiterOnly"
:
false
,
"buildIndexes"
:
true
,
"hidden"
:
false
,
"priority"
: 3,
#变为了3
"tags"
: {
},
"slaveDelay"
: NumberLong(0),
"votes"
: 1
},
{
"_id"
: 1,
"host"
:
"10.0.18.149:27017"
,
"arbiterOnly"
:
false
,
"buildIndexes"
:
true
,
"hidden"
:
false
,
"priority"
: 2,
#变为了2
"tags"
: {
},
"slaveDelay"
: NumberLong(0),
"votes"
: 1
},
{
"_id"
: 2,
"host"
:
"10.0.18.150:27017"
,
"arbiterOnly"
:
false
,
"buildIndexes"
:
true
,
"hidden"
:
false
,
"priority"
: 1,
#保持原来的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(
"58a568d2b205e55e0e0682d1"
)
}
}
5、停掉主(10.0.18.144)服务器的mongod进程
#killall mongod
然后在从服务器10.0.18.149上敲几下回车,从的角色立马由SECONDARY变为PRIMARY,如下:
Myrepl:SECONDARY>
Myrepl:SECONDARY>
Myrepl:SECONDARY>
Myrepl:PRIMARY>
Myrepl:PRIMARY>
Myrepl:PRIMARY> rs.status()
#查看状态
{
"set"
:
"Myrepl"
,
"date"
: ISODate(
"2017-02-16T10:12:40.419Z"
),
"myState"
: 1,
"term"
: NumberLong(2),
"heartbeatIntervalMillis"
: NumberLong(2000),
"optimes"
: {
"lastCommittedOpTime"
: {
"ts"
: Timestamp(1487239953, 1),
"t"
: NumberLong(2)
},
"appliedOpTime"
: {
"ts"
: Timestamp(1487239953, 1),
"t"
: NumberLong(2)
},
"durableOpTime"
: {
"ts"
: Timestamp(1487239953, 1),
"t"
: NumberLong(2)
}
},
"members"
: [
{
"_id"
: 0,
"name"
:
"10.0.18.144:27017"
,
"health"
: 0,
#原来的主服务器health为0
"state"
: 8,
"stateStr"
:
"(not reachable/healthy)"
,
#而且是不可达的状态
"uptime"
: 0,
"optime"
: {
"ts"
: Timestamp(0, 0),
"t"
: NumberLong(-1)
},
"optimeDurable"
: {
"ts"
: Timestamp(0, 0),
"t"
: NumberLong(-1)
},
"optimeDate"
: ISODate(
"1970-01-01T00:00:00Z"
),
"optimeDurableDate"
: ISODate(
"1970-01-01T00:00:00Z"
),
"lastHeartbeat"
: ISODate(
"2017-02-16T10:12:39.422Z"
),
"lastHeartbeatRecv"
: ISODate(
"2017-02-16T10:10:43.056Z"
),
"pingMs"
: NumberLong(0),
"lastHeartbeatMessage"
:
"Connection refused"
,
"configVersion"
: -1
},
{
"_id"
: 1,
"name"
:
"10.0.18.149:27017"
,
"health"
: 1,
"state"
: 1,
"stateStr"
:
"PRIMARY"
,
#原来的从服务器因为优先级高,变成了主
"uptime"
: 3446,
"optime"
: {
"ts"
: Timestamp(1487239953, 1),
"t"
: NumberLong(2)
},
"optimeDate"
: ISODate(
"2017-02-16T10:12:33Z"
),
"infoMessage"
:
"could not find member to sync from"
,
"electionTime"
: Timestamp(1487239853, 1),
"electionDate"
: ISODate(
"2017-02-16T10:10:53Z"
),
"configVersion"
: 2,
"self"
:
true
},
{
"_id"
: 2,
"name"
:
"10.0.18.150:27017"
,
"health"
: 1,
"state"
: 2,
"stateStr"
:
"SECONDARY"
,
"uptime"
: 3445,
"optime"
: {
"ts"
: Timestamp(1487239953, 1),
"t"
: NumberLong(2)
},
"optimeDurable"
: {
"ts"
: Timestamp(1487239953, 1),
"t"
: NumberLong(2)
},
"optimeDate"
: ISODate(
"2017-02-16T10:12:33Z"
),
"optimeDurableDate"
: ISODate(
"2017-02-16T10:12:33Z"
),
"lastHeartbeat"
: ISODate(
"2017-02-16T10:12:39.357Z"
),
"lastHeartbeatRecv"
: ISODate(
"2017-02-16T10:12:39.583Z"
),
"pingMs"
: NumberLong(0),
"syncingTo"
:
"10.0.18.149:27017"
,
"configVersion"
: 2
}
],
"ok"
: 1
}
注意:由上面可发现原来的主(18.144)已处于不健康状态(失连),而原来权重高的从(18.149)的角色变为PRIMARY,
权重低的从角色不变。不过要想让用户识别新主,还需手动指定读库的目标server!
6、原主恢复,新主写入的数据是否同步
在10.0.18.149这台新主服务器操作:
#mongo --host 10.0.18.149
MongoDB shell version v3.4.2
connecting to: mongodb:
//10
.0.18.149:27017/
MongoDB server version: 3.4.2
在此服务器建库、建集合、插入数据
Myrepl:PRIMARY> show dbs;
admin 0.000GB
local
0.000GB
testdb 0.000GB
Myrepl:PRIMARY> use testdb2
switched to db testdb2
Myrepl:PRIMARY> db
testdb2
Myrepl:PRIMARY> db.createCollection(
'test2'
)
{
"ok"
: 1 }
Myrepl:PRIMARY> show dbs
admin 0.000GB
local
0.000GB
testdb 0.000GB
testdb2 0.000GB
Myrepl:PRIMARY> db.testdb2.insert({
"name"
:
"repl2"
,
"time"
:
"17021618"
})db.testdb2.insert({
"name"
:
"repl2"
,
"time"
:
"17021618"
})
WriteResult({
"nInserted"
: 1 })
Myrepl:PRIMARY> db.testdb2.findOne()
{
"_id"
: ObjectId(
"58a57cd471c4e5a43bfa11fa"
),
"name"
:
"repl2"
,
"time"
:
"17021618"
}
在第二台从服务器10.0.18.150查看数据
#mongo --host 10.0.18.150
MongoDB shell version v3.4.2
connecting to: mongodb:
//10
.0.18.150:27017/
MongoDB server version: 3.4.2
Myrepl:SECONDARY> show dbs;
2017-02-16T18:21:23.860+0800 E QUERY [thread1] Error: listDatabases failed:{
"ok"
: 0,
"errmsg"
:
"not master and slaveOk=false"
,
"code"
: 13435,
"codeName"
:
"NotMasterNoSlaveOk"
} :
_getErrorWithCode@src
/mongo/shell/utils
.js:25:13
Mongo.prototype.getDBs@src
/mongo/shell/mongo
.js:62:1
shellHelper.show@src
/mongo/shell/utils
.js:755:19
shellHelper@src
/mongo/shell/utils
.js:645:15
@(shellhelp2):1:1
Myrepl:SECONDARY> rs.slaveOk()
Myrepl:SECONDARY> show dbs
admin 0.000GB
local
0.000GB
testdb 0.000GB
testdb2 0.000GB
也可以看到第二台从服务器已经从新主服务器复制了数据!
然后将原来的主服务器10.0.18.144的mongod进程启动,在新主服务器10.0.18.149回车查看如下:
Myrepl:PRIMARY>
2017-02-16T18:24:06.037+0800 I NETWORK [thread1] trying reconnect to 10.0.18.149:27017 (10.0.18.149) failed
2017-02-16T18:24:06.039+0800 I NETWORK [thread1] reconnect 10.0.18.149:27017 (10.0.18.149) ok
Myrepl:SECONDARY>
Myrepl:SECONDARY>
可以看到状态由原来的Myrepl:PRIMARY变为了Myrepl:SECONDARY
最后登录原主服务器查看数据是否存在
#mongo --host 10.0.18.144
MongoDB shell version v3.4.2
connecting to: mongodb:
//10
.0.18.144:27017/
MongoDB server version: 3.4.2
Myrepl:PRIMARY> show dbs
#可以看到testdb2库已经存在了
admin 0.000GB
local
0.000GB
testdb 0.000GB
testdb2 0.000GB
Myrepl:PRIMARY> use testdb2
switched to db testdb2
Myrepl:PRIMARY> db.testdb2.findOne()
#数据也可以查看到
{
"_id"
: ObjectId(
"58a57cd471c4e5a43bfa11fa"
),
"name"
:
"repl2"
,
"time"
:
"17021618"
}
说明:由上面可知,当权重更高的原主恢复运行了,在新主期间写入的新数据同样同步到了原主上,即副本集成功实现了负载均衡的目的。
7、为副本集开启认证功能
说明:在配置副本集的时候,没有开启认证功能,为了安全起见,配置好副本集之后,需要开启。
注:单服务器,启动时添加--auth参或者在配置文件中配置authorization: enabled开启开启验证。
副本集服务器,开启--auth参数或者在配置文件中配置authorization: enabled的同时,必须指定keyfile参数,节点之间的通讯基于该keyfile,key长度必须在6到1024个字符之间,最好为3的倍数,不能含有非法字符!
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
|
a、首先在主服务器创建数据库管理员用户 Myrepl:PRIMARY> use admin switched to db admin Myrepl:PRIMARY> db.createUser({user: "root" , pwd : "test123" ,roles:[ "userAdminAnyDatabase" ]}) Successfully added user: { "user" : "root" , "roles" : [ "userAdminAnyDatabase" ] } Myrepl:PRIMARY> show collections; #查看集合 system. users system.version Myrepl:PRIMARY> show users ; #查看用户 { "_id" : "admin.root" , "user" : "root" , "db" : "admin" , "roles" : [ { "role" : "userAdminAnyDatabase" , "db" : "admin" } ] } Myrepl:PRIMARY> db.system. users . find () #查看用户的详细信息 { "_id" : "admin.root" , "user" : "root" , "db" : "admin" , "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "YUtAugkOXigZNs0Y8sO+aA==" , "storedKey" : "SGcT/CXln2oV0yCg0AP6GUgNhLQ=" , "serverKey" : "ntBaZ36ulyNa7QuiLiXpclXb1TU=" } }, "roles" : [ { "role" : "userAdminAnyDatabase" , "db" : "admin" } ] } 然后为testdb库创建一个具有读写权限的普通用户 Myrepl:PRIMARY> use testdb switched to db testdb Myrepl:PRIMARY> db.createUser({user: "testuser1" , pwd : "123456," ,roles:[ "readWrite" ]}) Successfully added user: { "user" : "testuser1" , "roles" : [ "readWrite" ] } Myrepl:PRIMARY> show users ; { "_id" : "testdb.testuser1" , "user" : "testuser1" , "db" : "testdb" , "roles" : [ { "role" : "readWrite" , "db" : "testdb" } ] } 然后创建系统管理员用户 Myrepl:PRIMARY> use admin switched to db admin Myrepl:PRIMARY> db.createUser({user: "system" , pwd : "123456" ,roles:[{role: "root" ,db: "admin" }]}) Successfully added user: { "user" : "system" , "roles" : [ { "role" : "root" , "db" : "admin" } ] } Myrepl:PRIMARY> show users ; { "_id" : "admin.root" , "user" : "root" , "db" : "admin" , "roles" : [ { "role" : "userAdminAnyDatabase" , "db" : "admin" } ] } { "_id" : "admin.system" , "user" : "system" , "db" : "admin" , "roles" : [ { "role" : "root" , "db" : "admin" } ] } b、在主服务器生成keyfile文件 #cd /usr/local/mongodb #进入mongodb安装目录 #openssl rand -base64 21 > keyfile #创建一个keyfile(使用openssl生成21位base64加密的字符串) #chown mongod.mongod keyfile #修改属主属组 #chmod 600 keyfile #修改权限为600 注意:上面的数字21,最好是3的倍数,否则生成的字符串可能含有非法字符,认证失败! c、将keyfile的内容写入到其他2台从服务器对应目录,并修改属性和权限 d、在三台服务器的mongod.conf配置文件中添加认证参数 security: keyFile: "/usr/local/mongodb/keyfile" #keyfile路径 clusterAuthMode: "keyFile" #认证模式 authorization: enabled #开启认证 然后重启mongod进程,然后查看 #mongo --host 10.0.18.144 MongoDB shell version v3.4.2 connecting to: mongodb: //10 .0.18.144:27017/ MongoDB server version: 3.4.2 Myrepl:PRIMARY> show dbs 2017-02-17T17:35:42.675+0800 E QUERY [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "not authorized on admin to execute command { listDatabases: 1.0 }" , "code" : 13, "codeName" : "Unauthorized" } : _getErrorWithCode@src /mongo/shell/utils .js:25:13 Mongo.prototype.getDBs@src /mongo/shell/mongo .js:62:1 shellHelper.show@src /mongo/shell/utils .js:755:19 shellHelper@src /mongo/shell/utils .js:645:15 @(shellhelp2):1:1 可以看到无法执行show dbs,提示没有认证 Myrepl:PRIMARY> use admin switched to db admin Myrepl:PRIMARY> db.auth( "root" , "test123" ) #认证通过 1 Myrepl:PRIMARY> show dbs; #认证之后,查看就ok了 admin 0.000GB local 0.001GB testdb 0.000GB testdb2 0.000GB Myrepl:PRIMARY> show users ; { "_id" : "admin.root" , "user" : "root" , "db" : "admin" , "roles" : [ { "role" : "userAdminAnyDatabase" , "db" : "admin" } ] } { "_id" : "admin.system" , "user" : "system" , "db" : "admin" , "roles" : [ { "role" : "root" , "db" : "admin" } ] } 同样,在从服务器上想要查看数据,也需要认证通过后才可以! 8、其他 重新设置副本集命令: rs.stepDown() cfg = rs.conf() cfg.members[n].host= 'new_host_name:prot' rs.reconfig(cfg) 副本集所有节点服务器总数必须为奇数,服务器数量为偶数的时候,需要添加一个仲裁节点,仲裁节点不参与数副本集,只有选举权。 #添加仲裁节点 rs.addArb( "10.0.18.111:27017" ) 复制(副本集)相关请查阅中文网站 #################################### MongoDB查看复制集状态 /opt/app/mongodb3/bin/mongostat --host x.x.x.114:27017 -umongodbuser -pxxx --authenticationDatabase=admin --discover -n 3 1 insert query update delete getmore command % dirty % used flushes vsize res qr|qw ar|aw netIn netOut conn set repl time x.x.x.113:27017 *16 95 *27 *0 0 74|0 0.1 80.0 0 15.5G 14.2G 0|3 1|0 17k 964k 556 km_tv SEC 11:27:50 x.x.x.114:27017 *16 81 *27 *0 0 75|0 0.1 80.0 0 15.4G 14.1G 0|0 1|0 16k 745k 558 km_tv SEC 11:27:50 x.x.x.115:27017 17 *0 57 *0 79 212|0 0.4 49.8 0 9.7G 8.4G 0|0 1|0 360k 694k 518 km_tv PRI 11:27:50 x.x.x.113:27017 *12 85 *29 *0 0 81|0 0.2 80.0 0 15.5G 14.2G 0|0 2|0 17k 618k 556 km_tv SEC 11:27:52 x.x.x.114:27017 *12 80 *29 *0 0 80|0 0.1 80.0 0 15.4G 14.1G 0|0 1|0 16k 697k 558 km_tv SEC 11:27:51 x.x.x.115:27017 11 *0 52 *0 68 181|0 0.5 49.8 0 9.7G 8.4G 0|0 1|0 292k 980k 515 km_tv PRI 11:27:52 x.x.x.113:27017 *12 81 *35 *0 0 86|0 0.2 80.0 0 15.5G 14.2G 0|0 1|0 16k 906k 556 km_tv SEC 11:27:53 x.x.x.114:27017 *12 90 *35 *0 0 83|0 0.1 80.0 0 15.4G 14.1G 0|0 1|0 17k 16m 558 km_tv SEC 11:27:52 x.x.x.115:27017 12 *0 64 *0 76 199|0 0.5 49.8 0 9.7G 8.4G 0|0 1|0 271k 548k 515 km_tv PRI 11:27:53 各字段解释说明: insert /s : 官方解释是每秒插入数据库的对象数量,如果是slave,则数值前有*,则表示复制集操作 query /s : 每秒的查询操作次数 update /s : 每秒的更新操作次数 delete /s : 每秒的删除操作次数 getmore /s : 每秒查询cursor(游标)时的getmore操作数 command : 每秒执行的命令数,在主从系统中会显示两个值(例如 3|0),分表代表 本地|复制 命令 注: 一秒内执行的命令数比如批量插入,只认为是一条命令(所以意义应该不大) dirty: 仅仅针对WiredTiger引擎,官网解释是脏数据字节的缓存百分比 used: 仅仅针对WiredTiger引擎,官网解释是正在使用中的缓存百分比 flushes: For WiredTiger引擎:指checkpoint的触发次数在一个轮询间隔期间 For MMAPv1 引擎:每秒执行fsync将数据写入硬盘的次数 注:一般都是0,间断性会是1, 通过计算两个1之间的间隔时间,可以大致了解多长时间flush一次。 flush开销是很大的,如果频繁的flush,可能就要找找原因了 vsize: 虚拟内存使用量,单位MB (这是 在mongostat 最后一次调用的总数据) res: 物理内存使用量,单位MB (这是 在mongostat 最后一次调用的总数据) 注:这个和你用 top 看到的一样, vsize一般不会有大的变动, res会慢慢的上升,如果res经常突然下降,去查查是否有别的程序狂吃内存。 qr: 客户端等待从MongoDB实例读数据的队列长度 qw:客户端等待从MongoDB实例写入数据的队列长度 ar: 执行读操作的活跃客户端数量 aw: 执行写操作的活客户端数量 注:如果这两个数值很大,那么就是DB被堵住了,DB的处理速度不及请求速度。看看是否有开销很大的慢查询。如果查询一切正常,确实是负载很大,就需要加机器了 netIn:MongoDB实例的网络进流量 netOut:MongoDB实例的网络出流量 注:此两项字段表名网络带宽压力,一般情况下,不会成为瓶颈 conn: 打开连接的总数,是qr,qw,ar,aw的总和 注:MongoDB为每一个连接创建一个线程,线程的创建与释放也会有开销,所以尽量要适当配置连接数的启动参数,maxIncomingConnections,阿里工程师建议在5000以下,基本满足多数场景 set : 副本集的名称 repl: 节点的复制状态 M ---master SEC ---secondary REC ---recovering UNK ---unknown SLV ---slave RTR ---mongs process("router') ARB ---arbiter time : 当前时间 9、不停机状态下复制集主节点切换 注意:这里是在三台复制集的mongdob priority 全是1 的情况下!!! a、冻结其中的一个从节点,使其不参与到与primary的内部选举工作 进入到mongodb执行以下命令: (单位:秒) rs.freeze(120) b、重启主节点的mongodb进程 (在2分钟之内完成) c、第三个slave 节点 就被选举成为了主节点 rs.status() 查看状态 |
五、复制集其他一些用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
1、查看副本集成员数据同步(延迟)情况 mongo>db.printReplicationInfo() mongo>db.printSlaveReplicationInfo() #最好在secondary上执行 mongo>rs.printReplicationInfo() mongo>rs.printSlaveReplicationInfo() #最好在secondary上执行 mongo>use local >db.slaves. find () 在主节点上跟踪延迟: local .slaves该集合保存着所有正从当前成员进行数据同步的成员,以及每个成员的数据新旧程度。 登录主节点 >use local >db.slaves. find ()、 查看其中每个成员对应的 "syncedTo" :{ "t" :9999999, "i" :32} 部分即可知道数据的同步程度。 2、主节点降为secondary mongo>use admin mongo>rs.stepDown(60) #单位为 秒 3、锁定指定节点在指定时间内不能成为主节点(阻止选举) mongo>rs.freeze(120) #单位为 秒 释放阻止 mongo>rs.freeze(0) |
本文整体出自http://blog.51cto.com/linuxg/1897727
参考
http://msiyuetian.blog.51cto.com/8637744/1722406
http://www.osyunwei.com/archives/9313.html
http://blog.csdn.net/zhaowenzhong/article/details/51899093