上一篇文章介绍了MongoDB的yum安装方式、配置文件、创建用户等基础知识,本篇介绍MongoDB的源码安装以及复制集相关!

一、使用源码安装MongoDB

官网下载地址:http://www.mongodb.org/downloads

本次实验使用的mongodb版本是mongodb-linux-x86_64-rhel62-3.4.2.tgz
安装步骤如下:
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复制原理

官方说明:
The minimum recommended configuration for a replica set is a three member replica set with three data-bearing members: one primary and two 
secondary members

mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。mongodb各个节点常见的搭配方式为:一主一从、一主多从。

主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致,如下图:

MongoDB 复制相关介绍_第1张图片注:以上图片参考自互联网。

大致流程是:客户端从主节点读取数据,在客户端写入数据到主节点时,主节点与从节点进行数据交互保障数据的一致性。

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,复制集将无法提供写服务,处于只读状态。如下图:

MongoDB 复制相关介绍_第2张图片

通常建议将复制集成员数量设置为奇数,从上表可以看出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的倍数,不能含有非法字符!

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、查看副本集成员数据同步(延迟)情况
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://msiyuetian.blog.51cto.com/8637744/1722406

http://www.osyunwei.com/archives/9313.html

http://blog.csdn.net/zhaowenzhong/article/details/51899093

不足之处,请多多指出!