【MongoDB权威指南】七

七 创建副本

副本

使用复制把数据副本保存到多台服务器上。

副本集是一组服务器,其中有一个主服务器,用于处理客户端请求;还有多个备份服务器用于保存数据副本。如果主服务器崩溃,备份服务器回自动其中一台位新的主服务器。

创建副本集

启动shell但是不连接到任何mongod。

1
mongo --nodb

笔者这里使用的是windows10的cmd可以正常执行成功。如果在windows下使用cmder,不知道为啥会报错。

1
2
3
4
5
6
// 创建副本集,这里原书中没有指定name字段,会导致后面两句话执行不成功,所以这里根据报错猜测原因,加入了name字段
replicaSet = new ReplSetTest({'nodes': 3, 'name': 'test'})
// 启动3个mongod进程
replicaSet.startSet()
// 配置复制功能
replicaSet.initiate()

现在分别有了三个进程,运行在被分配的三个端口中。这三个端口怎么找呢?就在执行完第一行代码之后,控制台会返回一堆信息,其中接近是最底下的地方那里有个字段叫ports,就是那个。

把这个shell搁置在一旁,然后新启一个shell:

1
2
3
4
conn1 = new Mongo('localhost:20000')
primaryDB = conn1.getDB('test')
// 查看副本集状态
primaryDB.isMaster()

返回一个dict(js里是这么叫吗。。),其中hosts列表表示该副本集的所有端口,ismaster表示是不是主节点,primary字段表示主节点端口。

由于备份节点的数据可能会落后于主节点,所以备份节点一般是拒绝读取请求的,如果在备份节点上做查询,会得到一个错误提示。

如果真想访问备份服务器,敲

1
conn1.setSlaveOk()

最后,关闭副本集

1
replicaSet.stopSet()

自动故障转移

现在尝试关闭主节点,应该会看到其中一个备份节点会升级为主节点。

1
primaryDB.adminCommand({'shutdown': 1})

之后对第二个链接调用idMaster函数可以看到它已经变为主节点。

配置副本集

上一节是在自己的localhost上面尝试建立测试用的一个副本集,现在来看假如有三台linux机器,怎么在其上搭建副本集。

假设我们所在的机器是server-1,另有两台机器是server-2和server-3。

1
2
3
4
5
6
7
8
9
10
# on local machine(server-1)
mongod --replSet spock -f mongod.conf --fork
# link to server-2
ssh server-2
mongod --replSet spock -f mongod.conf --fork
exit
# link to server-3
ssh server-3
mongod --replSet spock -f mongod.conf --fork
exit

为了让三个mongod知道彼此的存在,需要设置。shell中键入:

1
2
3
4
5
6
7
8
config = {
'_id': 'spock',
'members': [
{'_id': 0, 'host': 'server-1:27017'},
{'_id': 1, 'host': 'server-2:27017'},
{'_id': 2, 'host': 'server-3:27017'},
]
}

注意,上面的spock是集合的名字,不要再傻傻去查翻译了。

之后对主节点进行初始化。

1
2
db = (new Mongo('server-1:27017')).getDB('test')
rs.initiate(config) // 等价于 db.adminCommand({'replSetInitiate': config})

其中rs这个变量是个全局变量,包含与复制相关的辅助函数。

需要确保每台机器可以连接到其他机器。

允许所有节点都在同一台机子上。

修改副本集配置

添加成员和移除成员

1
2
3
4
5
6
// 增加
rs.add('server-4:27017')
// 删除
rs.remove('server-4:27017')
// 查看当前状态
rs.config()

做remove操作的时候会在控制台打印很多错误信息,这很正常。

每次更新配置之后rs.config()返回的version字段都会加一。

假设要直接更改config表:

1
2
3
var config = rs.config()
config.members[1].host = 'server-2:4444'
rs.reconfig(config)

直接改。

主节点选举机制

太长不看。


下面讲副本集的组成。

同步

MongoDB的复制功能是使用操作日志oplog实现的,这份日志包含了主节点的每次写操作。oplog是主节点local数据库中的一个固定结合。

备份节点同样也拥有一份oplog。在执行完主节点的oplog之后,会更新进自己的oplog。为了避免过程中出问题重新执行oplog会重复生效操作,MongoDB执行oplog中的同一操作,只会执行一次。

通常来说每个写操作只影响一个文档,只会产生一条oplog日志。但是如果是执行像db.coll.remove()这种操作,就会产生多条数据(条数与coll中文档个数相当)

初始化同步

副本集中的成员启动之后,会检查是否能从某个成员那里进行同步(增量),如果不行,那么会从另一个成员那里进行完整的数据复制(全量)。这就是初始化同步的过程。

具体步骤如下:

  1. 删除自身所有的数据库
  2. 选择一个同步源,克隆数据
  3. 检查是否有文档需要重新克隆,重新克隆克隆过程中发生改动的文档,这是第一个oplog步骤
  4. 记录上一个步骤的oplog
  5. 创建索引
  6. 将创建索引时的oplog同步
  7. 切换到普通同步状态

处理落后的数据

如果数据落后于同步源台太多,那么这个备份节点就是陈旧的$stale$。

一个陈旧节点会尝试查看其他成员,如果有某个成员的oplog足够详尽,能够让它追上步伐,那就用他的oplog来同步数据。如果找不到这么一台机器,那就需要完全同步。

为了避免完全同步,最好让主节点能够用比较大的oplog,毕竟磁盘不用钱的。

心跳

为了知道副本集的状态,需要每隔2秒向其他成员发出一个心跳请求。最重要的功能之一就是让主节点知道自己是否符合条件,如果不符合就会退化成备份节点。

成员状态

  • STARTUP
  • STARTUP2
  • RECOVERING
  • ARBITER
  • DOWN
  • UNKNOWN
  • REMOVED
  • ROLLBACK
  • FATAL

选举

选举发生时机:其中一个成员无法到达(reach)主节点,它会向自己所有能reach的成员发通知,说希望被选举为主节点。其他节点会进行判断,看那个节点符不符合参与选举的条件(其中有一个原因可能是主节点其实在正常运行)。如果符合选举条件,就会开始选举。

一般而言选举非常快,可能只要几毫秒。运气不好也就几分钟。

回滚

在主节点在写入的时候崩溃之后,会选出新的节点。原主节点恢复之后会尝试同步刚才崩溃那次操作,如果这个操作在新的主节点并不存在,那么原主节点就会回滚。

如果回滚失败(数据量过大),就会进行完全同步。

关于副本集的更多内容在这里不做记录,等成为运维之后再说吧。

Buy Me A Coffee / 捐一杯咖啡的钱
分享这篇文章~
0%
//