复制集(Replication Set
),也叫副本集;做用是把一份数据同时保存在多台服务器上,保证数据的安全,不发生丢失;算法
经过指定 replSet
选项启动三台Mongo
服务器,端口号是 27017, 27018, 27019;指定加入的复制集名称为 demo
;mongodb
./bin/mongod --dbpath master/data --logpath master/log/0923.log --port 27017 --fork --replSet demo ./bin/mongod --dbpath slave1/data --logpath slave1/log/0923.log --port 27018 --fork --replSet demo ./bin/mongod --dbpath slave2/data --logpath slave2/log/0923.log --port 27019 --fork --replSet demo
此时,在三台服务器上分别键入rs.status()
命令,均报“未初始化”的错误;数据库
启动服务器的另一种方式是经过配置文件启动;举个例子,这里的27018
以配置文件启动;安全
配置文件以下所示:服务器
dbpath=/home/mongodb/slave1/data # 数据存放目录 logpath=/home/mongodb/slave1/log/0923.log # 日志存放路径 pidfilepath=/home/mongodb/slave1/slave1.pid # 进程文件,方便中止mongodb directoryperdb=false # 为每个数据库按照数据库名创建文件夹存放 logappend=true # 以追加的方式记录日志 replSet=demo # Replication Set 的名称 #bind_ip=127.0.0.1 # 限制只容许某一特定IP来访问,逗号隔开 port=27018 # 进程所使用的端口号,默认为27017 oplogSize=10000 # mongodb操做日志文件的最大大小。单位为Mb,默认为硬盘剩余空间的5% fork=true # 之后台方式运行进程 noprealloc=false # 不预先分配存储 smallfiles=false # 当提示空间不够时添加此参数
登入任意一台机器的 MongoDB
执行,由于是全新的复制集,因此能够任意进入一台执行;要是一台有数据,则须要在有数据上执行;要多台有数据则不能初始化。app
笔者选择在27017
这台MongoDB
服务器进行初始化spa
rs.initiate({ _id:'demo', // 复制集名称 members: // 复制集服务器列表 [ { _id:0, // 服务器的惟一 ID host:'192.168.1.168:27017' // 服务器的地址 }, { _id:1, host:'192.168.1.168:27018' }, { _id:2, host:'192.168.1.168:27019' }, ] });
这样,三台服务器都加入了demo
复制集;此时主节点27017
能够进行读写,从节点27018
和27019
不能够读写;全部的写操做只能在主节点上进行;.net
那如何才能在从节点27018
上读数据呢?在27018
上键入rs.slaveOk()
, 这样就能够进行读操做;命令行
注:能够经过rs.status()
查看复制集状态;经过rs.config()
查看复制集的配置信息;经过db.isMaster()
查看是不是主节点信息;unix
先启动想要添加的从节点服务器,笔者是本地的27020
服务器,同时指定相要加入的复制集名称;
./bin/mongod --dbpath slave3/data --logpath slave3/log/0923.log --port 27020 --fork --replSet demo
主节点27017
配置添加:
rs.add('192.168.1.168:27020');
此时,经过键入rs.status()
能够看到members
成员有四位,包括这台新的27020
从节点服务器;
若是想要移除27020
从节点服务器,便可成功从demon
复制集中移除该服务器;
rs.remove('192.168.1.168:27020');
目前环境:
27017
: Primary
主服务器27018
: SECONDARY
从服务器27019
: SECONDARY
从服务器在系统命令行上,手动drop
掉主服务器
lsof -i:27017 | sed '1d' | while read line do echo $line | awk '{print $2}' | xargs kill -9 done
或者在主服务器上执行下面的语句
db.shutdownServer()
此时,在经过rs.status()
查看环境
27017
: 离线(not reachable/healthy
)27018
: SECONDARY
从服务器27019
: Primary
主服务器这里,为何在主服务器27017
服务器挂了以后,会选择27019
作新的主服务器呢,这是由MongoDB
服务器内部的多数投票算法,即每台服务器会为secondary
从节点服务器进行投票,票数多的从节点服务器会成为新的Primary
服务器;
笔者这里很幸运,两台从节点服务器27018
和27019
都选择了27019
做为新的从节点服务器;另一种多是 27018
和27019
各得一票,这样会致使没法选举出新Primary
服务器; 因此建议复制集的服务器数目为奇数,若是碰巧是偶数,能够添加一台仲裁节点服务器;
仲裁节点是一种特殊的节点,它自己并不存储数据,主要的做用是决定哪个从节点在主节点挂掉以后提高为主节点,因此客户端不须要链接此节点。
仲裁节点服务器启动,
./bin/mongod --dbpath arbiter/data --logpath arbiter/log/0923.log --port 27021 --fork --replSet demo
主服务器的配置上添加仲裁节点信息:
demo:PRIMARY> rs.addArb('192.168.1.168:27021')
Primay
服务器将27017
服务器重启;
此时,在经过rs.status()
查看环境
27017
: SECONDARY
从服务器27018
: SECONDARY
从服务器27019
: Primary
主服务器27021
: Arbiter
仲裁服务器如何能将当前的服务器从27019
切换回27017
服务器呢?
经过修改priority
的值来实现(默认的优先级是1(0-100),priority
的值设的越大,就优先成为主);
在27019
主节点上执行
Primary > config=rs.conf() Primary > config.members[0].priority = 3 Primary > rs.reconfig(config)
注意:第2步members
大括号里面的成员和_id
是没有关系的,而是rs.conf
查出来节点的数值的顺序;
主节点的操做记录成为oplog
(operation log
)。 oplog
存储在一个系统数据库local
的集合oplog.rs
中,这个集合的每一个文档都表明主节点上执行的一个操做。咱们从新向主数据库服务器中插入一条数据,而后查看这个集合能够看到:
use local db.getCollection('oplog.rs').find({})
文档中的字段含义:
ts
:8字节的时间戳,由4字节unix timestamp
+ 4字节自增计数表示h
: 未知v
: 未知op
:操做类型,i
表示insert
;u
表示update
;d
表示delete
;c
表示db cmd
;n
表示no op
, 空操做,其会按期执行以确保时效性;ns
:操做所在的namespace
o
:操做所对应的document,即当前操做的内容(好比更新操做时要更新的的字段和值)
从服务器会按期从主服务器中获取oplog记录,而后在本机上执行!
对于存储oplog
的集合,MongoDB
采用的是固定集合,也就是说随着操做过多,新的操做会覆盖旧的操做!这样作也是有道理的,否则,这个集合占用的空间就没法估算了!咱们在启动服务时,能够经过选项--oplogSize
来指定这个集合的大小,单位是MB
,在Windows
平台下,默认MongoDB
会使用数据库安装分区可用空间的5%做为这个集合的大小!
' | 成为Primary |
对客户端可见 | 参与同步 | 延迟同步 | 复制数据 |
---|---|---|---|---|---|
Primary |
√ | √ | √ | ' | √ |
Secondary |
' | √ | √ | ' | √ |
Hidden |
' | ' | √ | ' | √ |
Delayed |
' | √ | √ | √ | √ |
Arbiters |
' | ' | √ | ' | ' |
Non-Voting |
√ | √ | ' | ' | √ |