您的位置:首页 > 数据库 > Mongodb

MongoDB学习笔记八:复制

2015-12-31 14:29 501 查看
【主从复制】
最基本的复制方式就是建立一个主节点和一个或多个从节点,每个从节点要知道主节点的地址。运行mongod --master启动主服务器。运行mongod --slave --source master_address启动从服务器,其中master_address是主节点的地址。
生产环境下会有多台服务器,这里简化在同一台机器上试验:
首先,给主节点简历数据目录,并绑定端口(10000):
$ mkdir -p ~/dbs/master
$ ./mongod --dbpath ~/dbs/master --port 10000 --master
接着设置从节点,要选择不同的目录和端口,并且用--source为从节点致命主节点的地址:
$ mkdir -p ~/dbs/slave
$ ./mongod --dbpath ~/dbs/slave --port 10001 --slave --source localhost:10000
所有从节点都从主节点复制内容。*目前还没有能够从从节点复制的机制(菊花链),原因就是从节点并不保存自己的oplog。
『选项』
主从复制有一些有用的选项:
· --only
在从节点上指定支付至特定某个数据库(默认复制所有数据库)。
· --slavedelay
用在从节点上,当应用主节点的操作时增加延时(单位是秒)。
· --fastsync
以主节点的数据快照为基础启动从节点。如果数据目录一开始是主节点的数据快照,从节点用这个选项启动要比做完整同步快很多。
· --autoresync
如果从节点与主节点不同步了,则自动更新同步。
· --oplogSize
主节点oplog的大小(单位是MB)。
『添加及删除源』
启动从节点时可以用--source指定主节点,也可以在shell中配置这个源。
假设主节点绑定了localhost:27017。启动从节点时可以不添加源,而是随后向sources集合添加主节点信息:
$ ./mongod --slave --dbpath ~/dbs/slave --port 27018
现在可以在shell中运行如下命令,将localhost:27017作为源添加到从节点上:
> use local
> db.sources.insert({"host" : "localhost:27017"})
在sources集合中插入源后,如果理科进行查询就能查到插入的文档:
> db.sources.find()
{
"_id" : ObjectId("4c1650c2d26b84cc1a31781f"),
"host" : "localhost:27017"
}
当完成同步时,该文档就被更新了:
> db.sources.find()
{
"_id" : ObjectId("4c1650c2d26b84cc1a31781f"),
"host" : "localhost:27017",
"source" : "main",
"syncedTo" : {
"t" : 1276530906000,
"i" : 1
},
"localLogTs" : {
"t" : 0,
"i" : 0
},
"dbsNextPass" : {
"test_db" : true
}
}
假设在生产环境下,想更改从节点的配置,改用prod.example.com为源,则可以用insert和remove来完成:
> db.sources.insert({"host" : "prod.example.com:27017"})
> db.sources.remove({"host" : "localhost:27017"})
注:要是切换的两个主节点有相同的集合,MongoDB会尝试合并,但不保证能正确合并。要是使用的一个从节点对应多个主节点,最好在主节点上使用不同的命名空间。
【副本集】
副本集(Replica Set)是有自动故障恢复功能的主从集群。
主从集群和副本集最为明显的区别是副本集没有固定的“主节点”:整个集群会选举出一个“主节点”,当其不能工作时则变更到其他节点。两者看上去非常相似:副本集总会有一个活跃节点(primary)和一个或多个备份节点(secondary)。
副本集最美妙的地方就是什么都是自动化的。
『初始化副本集』
从最简单的例子开始:两个服务器。
注:不能用localhost作为成员,所以得找到机器的主机名。在*NIX系统中,可以这样:
$ cat /etc/hostname
morton
首先,要为每一个服务器创建数据目录,选择端口:
$ mkdir -p ~/dbs/node1 ~/dbs/node2
在启动之前,还需要给副本集取个名字。名字是为了易于与别的副本集区分,也是为了方便的将整个集合视为一个整体。这里就命名为"blort"。
之后就启动服务器。--replSet选项是让服务器知道在这个"blort"副本集中还有别的同伴,位置在morton:10002(还没有启动):
$ ./mongod --dbpath ~/dbs/node1 --port 10001 --replSet blort/morton:10002
以同样的方式启动另一台:
$ ./mongod --dbpath ~/dbs/node2 --port 10002 --replSet blort/morton:10001
如果想添加第3台,下面两种方式都行:
$ ./mongod --dbpath ~/dbs/note3 --port 10003 --replSet blort/morton:10001
$ ./mongod --dbpath ~/dbs/note3 --port 10003 --replSet blort/morton:10001,morton:10002
副本集具有自检测功能:在其中指定单台服务器后,MongoDB就会自动搜索并连接其余的节点。
启动了几台服务器之后,日志就会告诉你副本集没有进行初始化。因为还差最后一步:在shell中初始化副本集。
在shell中,连接其中一个服务器(下面的例子中使用morton:10001)。初始化命令只能执行一次:
$ ./mongo morton:10001/admin
MongoDB shell version: 1.5.3
connecting to localhost:10001/admin
type "help" for help
> db.runCommand({"replSetInitiate" : {
"_id" : "blort",
"members" : [
{
"_id" : 1,
"host" : "morton:10001"
}
{
"_id" : 2,
"host" : "morton:10002"
}
]}})
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : true
}
这个初始化文档略显复杂,但一点点来看还是可以理解的:
"_id" : "blort"
副本集的名字。
"members" : [...]
副本集中的服务器列表。过后还能添加。每个服务器文档至少有两个键。
"_id" : N
每个服务器的唯一ID。
"host" : hostname
这个键指定服务器主机。
再连接一下别的机器,查询一下命名空间local.system.replset,会发现配置会在服务器之间相互传递。
注:关于副本集最新的文档:https://docs.mongodb.org/manual/replication/
『副本集中的节点』
有几种不同类型的节点存在于副本集中。
· standard
常规节点,它存储一份完整的数据副本,参与选举投票,有可能成为活跃节点。
· passive
存储了完整的数据副本,参与投票,不能成为活跃节点。
· arbiter
仲裁者只参与投票,不接受复制的数据,也不能成为活跃节点。
标准节点和被动节点之间的区别仅仅就是数量的差别;每个参与节点(非仲裁者)有个优先权。优先权为0则是被动的,不能成为活跃节点。有限制不为0,则按照由大到小选出活跃节点,优先值一样的话则看谁的数据比较新。所以,要是有两个优先值为1和一个优先值为0.5的节点,最后一个节点只有在前两个节点都不可用的时候才能成为活跃节点。
在节点配置中修改priority键,来配置成标准节点或者被动节点。
> members.push({
"_id" : 3,
"host" : "morton:10003",
"priority" : 40
});
默认有县纪委1,可以是1~1000(含)。
"arbiterOnly"键可以指定仲裁节点。
> members.push({
"_id" : 4,
"host" : "morton:10004",
"arbiterOnly" : true
});
被分界点会从活跃节点抽取oplog,并执行操作,就像活跃备份系统中的备份服务器一样。活跃节点也会写操作到自己的oplog,这样就能成为活跃节点了。oplog中的操作也包括严格递增的序号。通过这个序号来判断数据的时效性。
『故障切换和活跃节点选举』
如果活跃节点坏了,其余节点会选一个新的活跃节点出来。选举过程可以由任何非活跃节点发起。新的活跃节点由副本集中的大多数选举产生。仲裁节点也会参与投票,避免出现僵局(比如,当网络分割,参与节点被分成两半时)。新的活跃节点僵尸优先级最高的节点,优先级相同则数据较新的节点获胜。
活跃节点使用心跳来跟踪急群众有多少节点对其可见。如果不够半数,活跃节点会自动降为备份节点。这样就能防止活跃节点一直不放权,比如当网络分割后已经与集群隔离开的时候。
不论活跃节点何时变化,新活跃节点的数据就被假定为系统的最新数据。对其他节点(即原来的活跃节点)的操作都会回滚,即便是之前的活跃节点已经恢复工作了。为了完成回滚,所有节点连接新的活跃节点后要重新同步。这些节点会查看自己的oplog,找出其中活跃节点没有执行过的操作,然后向活跃节点请求这些操作影响的文档的最新副本。正在执行重新同步的节点被视为恢复中,在完成这个过程之前不能成为活跃节点候选者。
【在从服务器上执行操作】
从节点的主要作用是作为故障恢复机制,一方主节点数据丢失或者停止服务。除此之外,从节点可用作备份的数据源,也可以用来扩展读取性能,或是进行数据处理。
『读扩展』
用MongoDB扩展读取的一种方式是将查询放在从节点上,从而减轻主节点的负载。(但可能不完全同步。)
扩展读取本身很简单:还像往常一样设置主从复制,连接从服务器处理请求。唯一的技巧就是有个特殊的查询选项,告诉从服务器是否可以处理请求。(默认是不可以的。)这个选项交租slaveOkay,所有的MongoDB驱动程序都提供了一种机制来设置它。有些驱动程序还提供工具是的请求分不到从节点的过程自动化,但这个过程随驱动程序的不同而不同。
『用从节点做数据处理』
从节点的另外一个用途就是作为一种机制来减轻密集型处理的负载,或作为聚合,避免影响主节点的性能。用--master参数启动一个普通的从节点。同时使用--slave和-master有点矛盾。这意味着如果能对从节点进行写入、像往常一样查询,就把他作为一个普通的MongoDB主节点好了。从节点还是会从真正的主节点复制数据。这样,就可以对从节点执行阻塞操作而不影响主节点的性能。
【工作原理】
主节点记录在其上执行的所有操作。从节点定期轮询主节点或者这些操作,然后对自己的数据副本执行这些操作。由于和主节点执行了相同的操作,从节点就能保持与主节点的数据同步。
√『oplog』
主节点的操作记为oplog(operation log的简写)。oplog存储在一个特殊的数据库中,叫做local。oplog就在其中的oplog.$main集合里面。oplog中的每个文档都代表主节点上执行的一个操作。文档包含的键如下:
· ts
操作的时间戳。时间戳是一种内部类型,用于跟踪操作执行的时间。由4字节的时间戳和4字节的递增计数器构成。
· op
操作类型,只有1字节代码。(例如"i"代表插入)
· ns
执行操作的命名空间(集合名)。
· o
进一步制定要执行的操作的文档。对插入来说,就是要插入的文档。
oplog只记录改变数据库状态的操作。比如,查询就不存储在oplog中。这是因为oplog只是作为从节点与主节点保持数据同步的机制。
存储在oplog中的操作也不是万泉河主节点的操作一模一样的。这些操作在存储之前先要做等幂变换,也就是谁,这些操作在从服务器端多次执行,只要顺序是对的,就不会有问题。例如,使用"$inc"执行的增加更新操作,会被转换成"$set"操作。
oplog存储在固定集合中。由于新操作也会存储在oplog里,它们会自动替换旧的操作。这样就能保证oplog不超过预先设定的大小。启动服务器时可以用--oplogSize制定这个大小,单位是MB。默认情况下,64位的实例将使用oplog 5% 的可用空间。这个空间将在local数据库中分配,并在服务器启动时预先分配。
『同步』
从节点跟不上同步时,复制就会停下来,从节点需要重新做完整的同步。可以用{"resync" : 1}命令手动执行重新同步,也可以在启动从节点时使用--autoresync选项让其自动重新同步。重新同步代价高昂,所以要尽量避免,方法就是配置足够大的oplog。
『复制状态和本地数据库』
"本地数据库"用来存放所有内部复制状态,主节点和从节点都有。本地数据库的名字就是local,其内容不会被复制。这样就能确保一个MongoDB服务器只有一个本地数据库。
主节点上的复制状态还包括从节点的列表(从节点连接主节点时会执行handshake命令进行握手)。这个列表存放在slaves集合中:
> db.slaves.find()
{ "_id" : ObjectId("4c1287178e00e93d1858567c"), "host" : "127.0.0.1", "ns" : "local.oplog.$main", "syncedTo" : { "t" : 1276282710000, "i" : 1 } }
{ "_id" : ObjectId("4c128730e6e5c3096f40e0de"), "host" : "127.0.0.1", "ns" : "local.oplog.$main", "syncedTo" : { "t" : 1276282710000, "i" : 1 } }
从节点也在本地数据库中存放状态。在me集合中存放从节点的唯一标识符,在sources集合中存放源或节点的列表。
> db.sources.find()
{ "_id" : ObjectId("4c1287178e00e93d1858567b"), "host" : "localhost:27017", "source" : "main", "syncedTo" : { "t" : 1276283096000, "i" : 1 }, "localLogTs" : { "t" : 0, "i" : 0 } }
主节点和从节点都跟踪从节点的更新情况,这是通过存放在"syncedTo"中的时间戳来完成的。每次从节点查询主节点的oplog时,都会用"syncedTo"来确定哪些操作需要执行,或者查看是否已经跟不上同步了。
『阻塞复制』
开发者可以用getLastError的"w"参数来确保数据的同步性。这里运行getLastError会进入阻塞状态,知道N个服务器复制了最新的写入操作为止。
> db.runCommand({getLastError: 1, w: N});
当指定"w"选项后,还可以使用"wtimeout"选项,表示以毫秒为单位的超时。getLastError就能在上一个操作复制到N个节点超时时返回错误(默认情况下命令式没有超时的)。
【管理】——复制管理的一些概念
『诊断』
MongoDB包含很多有用的管理工具,用以查看复制的状态。
当连接到主节点后,使用db.printReplicationInfo函数:
> db.printReplicationInfo();
configured oplog size: 10.48576MB
log length start to end: 34secs(0.01hrs)
oplog first event time: Tue Mar 30 2010 16:42:57 GMT-0400 (EDT)
oplog last event time: Tue Mar 30 2010 16:43:31 GMT-0400 (EDT)
now: Tue Mar 30 2010 16:43:37 GMT-0400 (EDT)
这些信息是oplog的大小和oplog中操作的时间范围。例子中的oplog大约是10MB,仅能防止大约30秒的操作。差不多是时候为oplog扩容了。oplog的长度至少要能满足一次完整的重新同步。不然,从节点同步(或者重新同步)完成后发现已经跟不上了。
当连接从节点时,用db.printSlaveReplicationInfo()函数,能得到从节点的一些信息:
> db.printSlaveReplicationInfo();
source: localhost:27017
syncedTo: Tue Mar 30 2010 16:44:01 GMT-0400 (EDT) = 12secs ago (0hrs)
显示的是从节点的数据源列表,其中有数据之后时间。本例中只之后12秒。
『变更oplog的大小』
若一经发现oplog大小不合适,最简单的做法就是停掉主节点,删除local数据库的文件,用新的设置(--oplogSize)重新启动。过程如下:
$ rm /data/db/local.*
$ ./mongod --master --oplogSize size
size is specified in megabytes.
注:为大型的oplog预分配空间非常耗时,且可能导致主节点停机时间增加,所以尽可能手动预分配数据文件。关于停止复制的更多信息:https://docs.mongodb.org/manual/core/master-slave/
『复制的认证问题』
如果在复制中使用了认证,还需要做些配置,是的从节点能够访问主节点的数据。在主节点和从节点上都需要在本地数据库添加用户,每个节点的用户名和口令都是相同的。本地数据库的用户类似admin中的用户,能够读写整个服务器。
从节点连接主节点时,会用存储在local.system.users中的用户进行认证。最先尝试"repl"用户,若没有此用户,则用local.system.users中的第一个可用用户。所以,按照如下步骤配置主节点和从节点,用可靠的密码替换password,就能配置认证复制了:
> use local
switched to db local
> db.addUser("repl", password);
{
"user" : "repl",
"readOnly" : false,
"pwd" : "..."
}
从节点就可以复制主节点了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: