一、复制介绍
所谓的复制就是在多个主机之间同步数据的过程。
1、数据冗余及可用性
复制技术提供数据冗余及可用性,在不同的数据库服务器上使用多个数据副本,复制技术防止单个数据库服务器出现数据故障而出现数据丢失。通过设置从库,你能在上面进行灾难切换、数据备份、报表服务等。在某些应用场景下,你还能提高读的能力,客户端通过将读和写请求分发到不同的服务器上面。
2、MongoDB复制技术
副本集是一组共享相同数据集的mongod实例。当所有写请求发向主库,而其他从库从主库上应用这些操作,以保证所有成员数据的一致性。
副本集中只能有一个主库接收客户端的写请求。当主库接受写请求后,进行数据操作,这些操作都记录到操作日志中,称为oplog。从库复制这些oplog并应用这些操作以保证与主库的数据集一致。如果这时主库不可用,集群中一个合格的从库通过竞选接管成为新的主库。你也可以在复制集群中添加一个mongod实例,作为仲裁者存在,它并不维护任何数据,它存在的意义在于对集群中其他成员发来的竞选请求和心跳检测做出响应。它对服务器硬件要求并不高。它永远都作为仲裁者存在,无论主库down了,还是某个从库成为新主库。
1)异步复制
从库应用日志中数据操作,是一种异步的方式。
2)自动接管
当主库和其他成员通信过程中,超10秒还无法联通那么一个符合条件的从库通过竞选将成为新的主库。在mongodb3.2版本中,引入了新版本的复制协议,以减少接管的时间。
3)读操作
默认情况下,客户端的读请求是发往主库上的,但是可以设置将客户端的读请求发送到从库上,但是由于复制技术采用的异步方式,这意味着在从库上读到的数据有时会产生延迟,与主库数据出现不一致。
二、复制实践
1、副本集中成员
主要包括三个:主库、从库、仲裁者。在一个副本集群中,对成员个数的最低要求是:一个主库、一个从库、一个仲裁者。但是大多数实际应用中是一个主库、两个从库。在3.0版本中一个集群中最多可以达到50个成员,在3.2版本中可以有12个成员。
1)主库
在一个副本集群中,只能存在一个主库,接收所有写请求。MongoDB应用写操作到数据文件中并记录操作到日志文件oplog中。从库成员复制这些oplog日志并应用操作到他们的数据集中。在集群中,所有成员都能接收读请求,但是默认上应用程序的读请求直接被发送到主库上。当主库不可用了,这就触发了竞选,会在剩下的从库中选择一个新主库。
在某些情景下,会有两个节点有那么一瞬间认为他们自己是主库,但是他们最多只有一个能够完成写操作,它就是目前的主库,并且另外一个是前主库还没有觉察它被降级了,典型的由于是网络分区。当这种情况出现时,连接到前主库的客户端也许会察觉到过期数据,最后进行回滚。
2)从库
为了复制数据,从库采用异步的方式,复制主库上的oplog并应用日志中操作到自己的数据集。一个主从集群环境中可以存在多个从库。
- Priority 0 Replica Set Members
当从库设置优先级为0,表示这个从库不能成为主库,因为优先级为0的成员无法参加竞选。
- Hidden Replica Set Members
隐藏从库是对应用程序不可见的。前提必须是不能变成主库,也就是优先级为0
- Delayed Replica Set Members
设置某个从库的数据延迟主库多久,主要用来防止人为错误时进行恢复使用的,也就是当做备份。前提是延迟从库必须优先级为0,且为隐藏从库。
3)仲裁者
它没有数据集并且不能成为主库,它的存在可以允许主从复制集群中成员数为奇数,因为它总有一个投票权。
2、副本集部署架构
复制集群的架构能够影响集群的容量及性能。标准的生产环境部署架构是一个具有三个成员的复制集群,能够很好的提供容错和冗余能力。一般而言,我们要避免复杂,凡事根据实际的应用需求设计集群架构。
下面介绍几种常用的架构:
1)具有三成员复制集群
复制集群最低要求需要有三个成员,在三成员架构中,分为一主两从和一主一从一仲裁者。
- 一主两从模式:当主库不可用,两个从库通过竞选成为新主库
- 一主一从一仲裁:当主库不可用,这个唯一从库将会成为新主库
下面我们就对常用的一主两从进行配置部署:
【实验环境】:
主机IP 主机名 端口 功能说明
192.168.245.129 node1 27017 primary
192.168.245.131 node2 27017 secodary
192.168.245.132 node3 27017 secodary
1.1)在三台独立的主机上安装mongodb,请参考:http://www.cnblogs.com/mysql-dba/p/5033242.html
1.2)确保三台主机上的mongodb实例相互能够连接上。具体方法如下:
- 在node1主机上进行:
mongo --host 192.168.245.131 --port 27017 mongo --host 192.168.245.132 --port 27017
- 在node2主机上进行:
mongo --host 192.168.245.129 --port 27017 mongo --host 192.168.245.132 --port 27017
- 在node3主机上进行:
mongo --host 192.168.245.129 --port 27017 mongo --host 192.168.245.131 --port 27017
1.3)启动所有主机上mongod服务,特别的需要加上--replSet "rs0" 参数,指定复制集群名称。
mongod --dbpath=/data/db --fork --logpath=/data/log/mongodb.log --replSet "rs0" #一个集群上成员replSet名字必须一样
1.4)连接到mongo shell环境中,进行复制配置文件初始化:
rs.initiate() #这个操作只需要在一台上进行,一般都是在主库上进行
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "node1:27017",
"ok" : 1
}
1.5)确认复制集群的配置初始化
rs0:SECONDARY> rs.conf() { "_id" : "rs0", "version" : 1, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "node1:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 } } }
1.6)在主库上添加其他成员:
rs0:PRIMARY> rs.add("192.168.245.131") { "ok" : 1 } rs0:PRIMARY> rs.add("192.168.245.132") { "ok" : 1 }
1.7)使用rs.status()方法查看这时集群的状态:
rs0:PRIMARY> rs.status() { "set" : "rs0", "date" : ISODate("2015-12-06T01:31:03.278Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "node1:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 6180, "optime" : { "ts" : Timestamp(1449359583, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2015-12-05T23:53:03Z"), "electionTime" : Timestamp(1449359499, 2), "electionDate" : ISODate("2015-12-05T23:51:39Z"), "configVersion" : 3, "self" : true }, { "_id" : 1, "name" : "192.168.245.131:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 5888, "optime" : { "ts" : Timestamp(1449359583, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2015-12-05T23:53:03Z"), "lastHeartbeat" : ISODate("2015-12-06T01:31:01.809Z"), "lastHeartbeatRecv" : ISODate("2015-12-06T01:31:02.855Z"), "pingMs" : NumberLong(0), "syncingTo" : "node1:27017", "configVersion" : 3 }, { "_id" : 2, "name" : "192.168.245.132:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 2514, "optime" : { "ts" : Timestamp(1449359583, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2015-12-05T23:53:03Z"), "lastHeartbeat" : ISODate("2015-12-06T01:31:01.598Z"), "lastHeartbeatRecv" : ISODate("2015-12-06T01:31:01.121Z"), "pingMs" : NumberLong(2), "configVersion" : 3 } ], "ok" : 1 }