Mongodb复制集

复制机制一直是数据库高可用性设计的重要实现手段。复制机制的优点有:

  • 主从服务器读写分离,降低主服务器的访问压力
  • 备份工作放在从服务器,避免备份操作导致主服务器被锁
  • 当主服务器出现故障,可以快速切换到从服务器,减少宕机时间。

mongodb提供了两种复制机制:

  • 主从复制(官方已不推荐)
  • 复制集

接下来,我们尝试从无到有建立一个复制集,并参与到建立以后的读写分离,主从切换,故障转移,增减节点等运维工作。

1. 准备工作

我们在一台机器上建立拥有三个节点的mongodb复制集,它们相应的参数如下:

配置参数 dbpath port
server0 /data/mongo/data/r0 28010
server1 /data/mongo/data/r1 28011
server2 /data/mongo/data/r2 28012

同时,共用参数:

  • logpath: /data/mongo/log/mongo.log
  • key: /data/mongo/key/key-file

这里要做这么几个事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1. data,log文件夹的创建
mkdir -pv /data/mongo/data/r0
......

//2. key-file文件,注意权限,不能太OPen
openssl rand -base64 741 > mongodb-keyfile
chmod 600 mongodb-keyfile

//3. 为了方便,把启动参数全部写到相应的3个配置文件(s0.conf, s1.conf , s2.conf)里面

dbpath = /data/mongo/data/r0
port=28010
logpath = /data/mongo/log/mongo.log
logappend = true
fork = true
rest = true
replSet=rs0 ##replSet的名字,其他节点要一样
keyFile=/data/mongo/key/mongodb-keyfile

2. 启动mongod

这里要把三个节点依次启动

1
2
3
mongod -f /data/mongo/conf/s0.conf
mongod -f /data/mongo/conf/s1.conf
mongod -f /data/mongo/conf/s2.conf

3. 连接到s0,把所有的成员加入

1
2
3
4
5
6
7
8
9
10
11
12
//连接
mongo -port 28010;

//初始化节点
var config_str={
_id:"rs0",members:[
{_id:0,host:"127.0.0.1:28010"},
{_id:1,host:"127.0.0.1:28011"},
{_id:2,host:"127.0.0.1:28012"}
]
};
rs.initiate(config_str);

到此,复制集应该已经建完了。

4. 查看复制集情况

复制集的情况查看有这么一些方面

4.1 查看配置,可以了解节点信息

rs.conf();

Alt text

4.2 查看复制集的同步情况

db.printSlaveReplicationInfo();
Alt text

4.3 当前是否是主节点

rs.isMaster()
Alt text

4.4 同步日志详情

db.printReplicationInfo();
Alt text

use local; db.oplog.rs.find();
Alt text

4.5 测试同步

上面的几点都只是看下整体配置情况,现在要测试同步机制是否运转起来了。

  • 首先在Primary节点中插入一条记录

    1
    2
    use my;
    db.Temp.insert({name:"jay"});
  • 连接任意一个SECONDARY节点,查看数据

    1
    2
    3
    mongo -port 28012;
    use my;
    db.Temp.find().sort({_id:-1}).limit(1);

很有可能,你会得到这个信息:
Alt text

这是因为SECONDARY节点还没有开启读操作权限,执行下面的命令,然后再查下:

1
2
rs.slaveOk();
db.Temp.find().sort({_id:-1}).limit(1);

Sets the slaveOk property for the current connection. Deprecated. Use readPref() and Mongo.setReadPref() to set read preference.
根据官方的说法,sleveOk()已经被废弃了,现在推荐readPref()|Mongo.setReadPref()|db.getMongo().setSlaveOk()来进行读优先设置

Alt text
数据已经出来了,说明整个复制集是已经正常运转的。

  • 可以再回头看看同步记录
    1
    db.oplog.rs.find().sort({ts:-1});

Alt text
可以看到刚刚的同步记录,op=i 表示插入操作。

5. 正常的运维

复制集建立以后,运维过程中经常会遇到的事情有这么几个方面:

  • 读写分离
  • 主从切换
  • 故障转移
  • 增减节点

5.1 读写分离

首先看看复制集设计的初衷:

A replica set is a group of mongod instances that host the same data set. One mongod, the primary, receives all write operations. All other instances, secondaries, apply operations from the primary so that they have the same data set.

Alt text

所以读写分离基本上是复制集的最主要的目的。通过读写分离,由Primary接收所有的写操作,读操作大部分由Secondary分担,这样数据库的性能将大大提升。

在建立复制集的过程中其实已经遇到了读写分离的问题,需要对Secondary节点进行读操作配置:

1
2
3
4
rs.slaveOk();//已废弃
db.getMongo().setSlaveOk()
cursor.readPref()
db.getMongo.setReadPref()

5.2 主从切换

什么时候需要主从切换?

当Primary节点负载过大,或者提供了一台性能更好的机器加入复制集,希望把新机器切换为Primary等,实际情况有很多种,主从切换有可能是经常发生的事情。

主从切换主要有两步

  • 冻结不参与切换的Secondary节点
  • 对当期主节点进行降级

下来来看看这个过程,当期的Primary是(s1,port 28011),我希望把(s0,port 28010切换为Primary)
step 1

1
2
3
// 对不参与切换的 s2 进行冻结
mongo -port 28012;
rs.freeze(60); // 冻结60秒

Alt text

step 2

1
2
3
//对当前的primary进行降级
mongo -port 28011
rs.stepDown(30); // 30秒内完成降级

Alt text
从图片上看已经降级了

step 3

1
rs.status();

Alt text
s0已经升级成功

5.3 故障转移

故障转移是高可用性系统的重要标准。而复制集机制就是故障转移的重要手段,下面来测试一下。

  • 首先,查看一下当前正在正常运行的mongod复制集实例。
    1
    ps -ef | grep mongod

Alt text

  • 现在我要把primary节点杀掉,看看会出现什么情况

    1
    kill -9 58090
  • 连上s1,查看相关当前复制集情况

    1
    2
    mongo -port 28011;
    rs.status();

Alt text
可以看到主节点已经不可用了,但是实际Secondary并没有自动选举出新的Primary,好奇怪,先放这里吧
实际情况是:我把s0重启,复制集居然自动选举了,现在s2是Primary

5.4 增减节点

增加节点有两种方式:

  • 通过oplog增加节点
  • 通过数据库快照和oplog综合增加节点
5.4.1 通过oplog增加节点

这个依赖于oplog数据的可靠性,应为oplog是一个CapCollection,数据可能不完整,所以一般不推荐使用。
当然这个操作也相对比较简单。

1
2
3
4
5
6
// 1. 启动一个新的待添加的节点
mongod -f /data/mongo/conf/s3.conf
// 2. 把这个节点加入
rs.add("127.0.0.1:28013");
//3. 静静等待同步结果
rs.status();
5.4.2 通过数据库快照+oplog增加节点

这个方法比较讨巧,原理就是先复制一份其他Secondary节点的数据库快照文件,然后在Primary加一条数据用于验证。最后把新节点加入复制集,等60秒查看刚刚的数据同步了没有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//1. 复制数据库文件快照
cp -rv /data/mongo/data/s1 /data/mongo/data/s3

//2. 连接primary,新增一条验证数据
mongo -port 28012;
use my;
my.Temp.insert({name:"test"});

// 3. 启动一个待添加的节点
mongod -f /data/mongo/conf/s3.conf

//4. 把这个节点加入
rs.add("127.0.0.1:28013");

//5. 静静等待同步结果
rs.status();

上面的过程我全程验证了一遍,懒得截图了。

6. 相关链接

7. 后续

花了一个下午学习,一个上午写完这篇笔记。其实还有一个很重要的点没有去研究,先记录到这里。

java driver 与复制集的交互