Mongodb Manual阅读笔记:CH2 Mongodb CRUD 操作
Mongodb Manual阅读笔记:CH3 数据模型(Data Models)
Mongodb Manual阅读笔记:CH4 管理
Mongodb Manual阅读笔记:CH5 安全性
Mongodb Manual阅读笔记:CH6 聚合
Mongodb Manual阅读笔记:CH7 索引
Mongodb Manual阅读笔记:CH8 复制集
Mongodb Manual阅读笔记:CH9 Sharding
Mongodb中的数据库复制集是一组mongod进程,维护了同一个数据集。复制集提供了冗余和高可用,是所有产品部署的基础。
复制是一个用来同步数据到多个服务的处理
复制提供了融合和高可用,多个数据拷贝放在不同的数据服务上,以免出现单点故障,就算出现硬件错误也可以恢复,当出现问题的时候,只要把其中一个拷贝来替换原来的即可。
有时候也可以使用复制,来做读写分离,客户端可以指定从不同的服务上读取。
复制集是一组mongod实例维护了同一份数据,primary接受所有的写入。Secondary从primary上获取这些写入然后应用到本地。
Primary接受所有client的写入操作,并且一个复制集只能有一个primary,这样才能提供严格的一致性。Primary把所有的写入操作都放在一个叫oplog的地方。
Secondary复制primary的oplog然后应用到自己的数据集上,secondary就是primary的一个影子,当primary不可用时,复制集会选一个secondary变成primary。客户端默认从primary读取数据,也可以通过制定读偏好,从secondary上读取。
当然你可以增加一个仲裁服务,仲裁没有数据,但是可以投票选出primary,如果你的复制集的成员是偶数的,可以增加一个仲裁,仲裁对硬件基本没什么要求。
Secondary从primary上应用操作是异步的,在primary之后应用到secondary,虽然有些成员还是没有,但是复制集还可以继续运行,唯一的问题是secondary不能反映当前的数据状态。
当primary不能和其他成员交互超过10s,复制集会试图选择一个成员变成primary。
复制集提供了一些选项来支持应用程序的需要,如,成员在多个数据中心,通过优先级来控制是否可以变成primary。
介绍了复制集的选项,配置和特性
复制集是一组mongodb提供的冗余和高可用,成员如下:
Primary,接受所有的写操作
Secondary,从primary上复制数据,唯一同一份数据。Secondary可以有额外的配置来用于特殊的使用。
还有一个就是仲裁,仲裁不维护数据,但是当当前primary不可用,仲裁可以投票给一个成员选出一个primary。
一个复制集最多只能有12个成员,只有7个成员可以投票。最小的复制配置是1个primary,1个secondary,1个仲裁,或者1个primary,2个secondary。
Primary是唯一一个可以接受写入的成员,写入到primary之后然后记录到primary的oplog,然后secondary复制这些日志应用这些操作。
所有成员都可以接受读操作,默认应用程序会直接从primary上读取。
复制集只能有一个primary,也必须要有一个,当当前的primary不可用,可以换一个。
Secondary维护了primary的数据备份,然后通过应用primary的oplog来异步的更新数据。一个复制集可以有多个secondary。
客户端不能写入到secondary只能从secondary上读取。当primary不可用,复制集会从secondary上选一个。
还可以配置secondary:
1.为了防止secondary变成primary,可以把优先级设为0
2.为了防止secondary读取可以把secondary设置为隐藏
3.用来维护一个历史的快照,用来恢复认为的错误。
优先级为0的secondary不能变成primary。
把优先级为0的作为准备:因为当数据大的时候新增一个节点有困难,准备节点可以很快的去替换现在不可用的节点。一般是不太用准备节点,但是在不同硬件设备,不同物理环境状况下,0优先级可以保证可以选到一个合格的primary。
0优先级和故障转移:考虑到潜在的故障,把优先级设置为0,并且保证你的主要数据中心包含了所有的合格可以变成primary的成员和可以投票的成员。
隐藏成员维护了primary的数据备份,但是对应用程序不可见。隐藏成员用来其他使用,隐藏成员难道优先级都为0,不能变成primary。使用db.isMaster()不显示隐藏成员,但是隐藏成员可以投票。
客户端的读取不能从隐藏成员上读取,隐藏成员一般用来做报表或者用来备份。如果用来备份,就要让隐藏成员尽量接近primary。
为了避免隐藏成员关闭mongod,可以使用db.fsyncLock()来刷新所有的写入,并且在备份期间锁定mongod。
延迟成员,是primary数据的一个延迟。如果出现人为的错误,可以使用延迟成员来恢复。
要求:
1.优先级必须为0,2.不许是隐藏的,3.可以投票。
延迟成员是延迟应用oplog,所以当选择延迟时间的时候要考虑以下2点:
1.必须大于等于你的维护窗口
2.必须小于oplog的能力。
仲裁不包含数据也不能成为primary,但是复制集可以增加一个仲裁来一起投票primary。仲裁可以让复制集成员变成奇数,但是不用复制数据。
当成员是偶数的时候再加仲裁。如果你把仲裁加入到一个奇数成员个数的集中,复制集可能会无法产生primary。
当启用auth的时候,仲裁会和其他成员交换凭据,仲裁使用keyfiles来验证到复制集。
交互:只有仲裁要投票,心跳,配置的时候才会和其他成员交互。当然也可以运行在可信任网络下。
复制集的结构影响了复制集的容量和能力,标准的复制集是3个成员,这个成员提供了冗余和故障切换。尽量避免复杂,但是也可以让你的应用程序来决定结构。
部署一个奇数成员:计数成员可以保证总是可以选出一个primary。如果成员是偶数的,那么假一个仲裁。仲裁只消耗很少的资源。
考虑错误容量:错误容量是有多少成员变的不可用了,但是还是可以选出primary。错误容量和复制集大小并不是直接相关的。也就是复制集的成员和选出primary的majority成员的差。
在复制集上面加成员并不能增加错误容量。
使用隐藏和延迟成员来做特定的需求:可以用来备份,报表,和认为错误的恢复。
读负载均衡:如果有很高的读请求,可以从secondary上读来减少primary的读请求。
在需求前加容量:如果有加能力的需求要在需求提出之间加,就是必须要有备用的能力。
地理位置上分布:为了保护主数据中心错误,至少要有一个节点在其他数据中心,并把这些成员的优先级设置为0
把majority成员放在同一个数据中心:如果复制集成员在不同的数据中心,网络问题会导致数据中心之间无法交互。对于复制数据来说,成员间必须可以交流,并保持
保证成员可以创建一个majority,确认majority并且选一个primary。并在一个数据中心保持复制集成员的majority。
使用Tag来确定操作目标:使用复制集tag来保证要操作某个复制集成员。
使用Journal来保护断电:可以使用journal来保护服务突然中断
有以下几种部署模式:3成员复制集,4个或者更多复制集,多地理位置复制集。
最小的结构就是3个成员,有2个方案2个secondary,1个primary或者1个secondary,1个仲裁,1个primary。
2个Secondary:这个方案任何时间都有2个数据备份,提供了错误容量和高可用。当primary不可用,会选一个secondary边长primary,然后继续运行,如果primary恢复会重新加入到复制集
1仲裁1Secondary:仲裁不保存数据,需求的资源也不是很大,但是限制了融合和错误容量。
但是这种方案,当primary,secondary任意一个不可用是,还是可以保持可用。如果primary不可用会选出一个primary。
尽管标准的复制集是3个成员,但是增加一个成员也就正价了能力和冗余,可以用来读写分离。
增加成员保证:
1.集合是奇数个投票成员
2.虽然最多有12个成员,但是只有7个可以投票。
3.优先级为0的不能成为primary
4.majority成员应该在主数据中心
在多个数据中心增加到复制集,增加了冗余和错误容量。在其他数据中心的成员应该把优先级设置为0 ,防止他们变成primary。
多地理位置结构应该:
1.一个primary在主数据中心
2.一个secondary在主数据中心,这个成员在任何时间变为primary
3.优先级为0的再第二个数据中心,这个成员不能变成primary
如果primary不可用,复制集会从主数据中心中选一个变成primary。如果数据中心不能相互连接,在第二个数据中心中的成员不能成为primary。
如果datacenter变成不可用,可以很快的通过手动方式来恢复第二个数据中心上的成员。
为了促成投票,主数据中心应该要保持绝大多数成员,并且保证复制集成员是奇数个,如果增加一个成员变成了偶数,就应该部署一个仲裁。
复制集使用自动故障转移来提供高可用性,高可用性可以让secondary在primary不可用时变成primary。
如果primary不可用,复制集会开始一个选举,来产生新的primary。在某些情况下故障转移需要请求rollback。
复制集选举,在故障转移时回滚。
复制集使用选举的方法来确定成员是否会变长primary,在初始化复制集之后也会选举,任何primary不可用也会选举。primary是唯一一个可以写入的成员。当primary不可用,复制集会中恢复正常状态。
心跳:如果心跳没有在10s中内返回,其他成员就会标记这个成员不可用
优先级:优先级影响选举,成员会给优先级最高的成员投票。所以优先级为0的不能成为primary。
当primary是优先级最高的,并且在最新oplog在10s只能复制集不会发起投票。如果一个更高的成员oplog和primary差距在10s只能,复制集会投票让给最高优先级的节点。
Optime:optime是oplog的时间戳,一个复制集成员不可能成为primary除非它的optime比其他成员都高。
连接:只有能连接到大多数成员的复制集成员才有可能成为primary
如果只有3个成员每一个都有一个vote,如果2个成员可以互相连接就可以选出来,如果2个成员不可用,剩下一个secondary不能变成primary。(因为票不够)。
网络分区:不再同一个网络会影响投票。如果primary被关闭,任何一方都没有形成大多数成员,那么就无法产生primary。为了避免这个状况保持却大多数成员在同一个数据中心。
投票触发事件:1.初始化一个复制集。2.secondary和primary连接丢失,3.primary关闭
以下状况下primary会关闭:
1.收到一个replSetStepDown命令
2.如果有1个成员合格变为primary并且优先级最高
3.如果primary不能和大多数的成员交互。
参与投票:每个成员有一个优先级来表示是否可以合格成为primary。默认每个成员的优先级为1。优先级表示最有可能成为primary的权重。通过指定优先级让有能力变成primary的成员的放在一个数据中心。
得到票数最多的成员变成primary,默认所有成员只有1个vote,只有在以下状态可以vote。PRIMARY, SECONDARY, RECOVERING, ARBITER, 和ROLLBACK。
否决投票:所有的成员都可以否决投票以下状况下会否决:
1.如果不是vote成员
2.在成员中并不是最新的
3.优先级比别人低
4.如果一个优先级为0 的成员是最接近primary的成员,这种状况下另外一个合格成员catch up状态,并试图成为primary
5.如果当前的primary比选出来的成员要新
非投票成员:非投票成员不能投票,但是可以否决结果。vote为0 即为非投票成员。尽量不要修改vote个数大于1,不然可能会出现问题。
rollback是把之前primary的写操作还原,如果primary在写操作结束之前就不可用了,那么就要回滚写入,使用rollback来维持数据的一致性。
mongodb试图避免回滚,但是往往都是secondary不能及时赶上primary才导致回滚。
收集回滚数据:当回滚发生,管理员可以决定是否应用这些回滚。mongodb会把回滚数据写在rollback下的bson文件中。
只有应用了回滚数据,成员才能回滚。使用bsondump读取,使用monogorestore来应用到新的primary。
避免复制集回滚:使用写注意保证写入操作传播到了复制集其他成员。
回滚限制:mongod实例不会回滚超过300MB的回滚数据,只能使用初始化同步来初始化
对于应用程序来说,复制集是透明的,默认客户端从primary上读写。当然客户端可以通过读偏好从其他成员上读取数据。但是secondary不能保证和primary的严格一致性。
为了保证一致性,你可以配置客户端和驱动来保证写入操作在完成前都已经写入到所有的成员上。
复制集的写注意,读偏好,读偏好过程。
写注意使用getLasterError命令来返回写入操作完成之后的信息。
默认写入操作只写到primary,通过getLastError的w参数让写入操作写入到secondary。当w为majority会写入到集群的大多数成员。
如果指定的w值大于成员个数,会导致操作一直被堵塞,当然可以指定超时时间,超时退出。
在复制集中可以通过getLastErrorDefault来修改默认的getLastError的行为。
cfg = rs.conf()
cfg.settings = {}
cfg.settings.getLastErrorDefaults = {w: "majority"}
rs.reconfig(cfg)
可以通过复制集tag来配置客户化写注意,并通过getLastErrorDefault,getLastErrorMode来使用。
{ "use": "reporting" }
{ "use": "backup" }
{ "use": "application" }
{ "use": "application" }
{ "use": "application" }
可以用来确保写操作应用到了2个不同的use tag的值。
cfg = rs.conf()
cfg.settings = { getLastErrorModes: { use2: { "use": 2 } } }
rs.reconfig(cfg)
db.runCommand( { getLastError: 1, w: "use2" } )
{ "disk": "ssd" }
{ "disk": "san" }
{ "disk": "spinning" }
使用这类tag不能指定到某个tag,可以用一下方式实现:
{ "disk": "ssd" }
{ "disk": "san", "disk.san": "san" }
{ "disk": "spinning" }
cfg = rs.conf()
cfg.settings = { getLastErrorModes: { san: { "disk.san": 1 } } }
rs.reconfig(cfg)
db.runCommand( { getLastError: 1, w: "san" } )
同时可以把这个配置设置为默认的
cfg = rs.conf()
cfg.settings.getLastErrorDefaults = { ssd: 1 }
rs.reconfig(cfg)
读偏好表示了如何从成员上读取数据。默认只从primary上读取,从primary上读取保证了读的是最新的数据,从secondary上读取提高的读的吞吐量,减少延迟但是数据不是最新的。
使用场景:
1.操作不会对前端应用的影响
2.多地理位置的应用可以从本地读取,减少网络延迟
3.在故障转移时,维护可用性。
Primary:默认方式,所有读取都从primary操作
primaryPreferred,优先选择primary,如果不可用从secondary上读取
secondary,从secondary上读取
secondaryPreferred,首选secondary
nearest,从最近的节点读取
可以在连接对象,数据库对象,collection和每个操作上指定读偏好模式。如果是shard复制集也可以用读偏好。
在shell中使用readPref光标方法来设置读偏好模式。
db.collection.find().readPref( { mode: 'nearest',
tags: [ {'dc': 'east'} ] } )
tag可以允许你指定客户化的读偏好和写注意。写注意和读偏好使用不同的方式来使用tag。读偏好使用tag的键值对,写注意使用有多少个不通知的tag。
tag和primary是不兼容的,只能用来选择secondary,但是nearest读取方式,当和tag绑定的时候,会现在选择指定tag最近的机器,可能是primary可能是secondary。
所有的接口使用相同的成员选择逻辑,来选择要读取的成员。
为了能能够确定操作的路由,应用程序定期更新他们存放复制集状态的视图,表明那个成员up,down,或者是primary。
当你使用非primary上读取的时候,驱动会确定使用什么成员。
1.对可用的成员分配一个队列,带入成员类型
2.剔除和tag不匹配成员
3.确定那个成员最靠近client的绝对值
4.使用ping的距离过滤绝对值列表最近的成员,默认是15s可以通过驱动的secondaryAcceptLatencyMS和--localThreshold或者localThreshold
5.随机选择一个成员来做读操作
然后驱动会关联线程连接到secondary成员。secondary读取的都是以前的数据。为了防止读的节点跳来跳去,第一次读之后驱动会指定一个成员,就不会从其他成员上读了,除非碰到一下问题:
1.使用不懂的读偏好执行
2.线程终端
3.客户端收到一个socket异常
当客户端发现有了一个新的primary,驱动会取消所有的线程到成员之间的连接。
mongod和驱动之间的连接有2个注意点:
1.客户端获取当前的结果,应该都从相同的节点读取。
2.客户端应该最小化当数据库不可访问的时间。
所以mongod和驱动:
1.只要连接之后尽量使用同一个连接
2.如果存在读偏好,如果连接的那个mongod丢失了应该重新连接一个。
重连对应用程序来说是透明的,若连接允许从secondary上读取,当重连后,会从不同的secondary相继获取2个读返回,更具各个secondary的状态,这些文档可以反映数据库的不同时间点的状态。
3.当连接3个符合标准的成员之后,就会报错,如果少于3个成员,连接了所有成员之后报错。
4.发现故障转移状态之后,驱动视图尽快刷新复制集状态。
在shard集群中,shard如果是复制集的话,也可以使用读偏好。和非shard的复制集一样。和非shard复制集不一样的是,在shard集群中,所有从客户端和shard的交互都要通过mongos,mongos是实际连接到所有成员的。然后mongos反映mongos 的读偏好并返回给客户端。
所有的mongos维护了自己的连接池,到复制集。所以:
1.如果不指定偏好为primary,默认mongos会重用连接,可能是不同的读偏好。所以最好显示指定读偏好。
2.所有nearest和延迟计算是值mongos到mongod之间。
成员同步数据是连续的,第一步,先初始化同步整个数据,第二部通过oplog来连续的同步自己的数据。
Oplog是特定的capped collect,oplog记录了所有数据库的修改操作。Mongo在primary上的写入都会被记录到oplog上,然后secondary复制oplog到自己的数据库再应用。
所有成员都可以从其他成员上获取oplog 然后再应用。
Oplog要保证,不管被应用多少次结果都是一样的,如初始化同步,rollback,shard块合并。
启动的时候mongodb使用默认大小创建,这个大小根据操作系统的细节来决定。一般情况下默认的已经够用,默认oplog大小是5%的空闲磁盘空间。也可以使用oplogSize选项来指定。
1.在linux64下默认大小为5%磁盘可用空间
2.OS X183MB
3.32系统,48MB
更新多个文档:为了保证多次应用依然一样,多文档更新必须转化为对单个文档的更新。造成oplog的要求。
删除和插入一样的数据:这样虽然不会造成collection的增长,但是会造成oplog增长。
大量的in-place更新:大量的in-place更新,数据库大量记录操作,但是不会增长collection
可以使用db.printReplicationInfo方法来查看oplog状态,可以通过在secondary成员上db.getReplicationInfo和复制的状态可以可以看到是否有刻意的delay。
复制集的数据同步有2中,1,初始化同步,2.使用不断的更新数据集。
初始化同步是复制所有数据从一个成员到另外一个成员。如果执行初始化同步,mongodb执行如下:
1.克隆所有数据库
2.应用所有的数据集
3.在collection创建索引。
初始化同步之后,复制成员从primary同步,当然同步对象是可以根据需要修改的。目标成员和源成员对buildIndexes设置必须相等。
在复制集中只有primary成员可以写,只有primary提供了严格的一致性。Journal提供了单实例写持久性。如果没有journal,mongodb中断,就必须假设数据库是在不可用状态。
Mongo可以把写入操作分批来提高并发。Mongodb以命令空间分组批处理,并且使用一组线程来应用,但是写操作应用到一个命名空间是顺序的。
但是每个批处理,会堵塞所有读。作为结果secondary不能返回任何数据来反应当前数据库状态,这个不会在primary存在。
为了提高oplog应用性能,mongodb取保存了数据的内存页。当应用oplog,预取可以最小化mongodb write锁的时间。
复制集优于主从复制,复制集可以有很多slave节点,主从复制可以转化复制集,也可以把复制集转化为主从复制。
2个mongod一个主,一个从启动
mongod --master --dbpath /data/masterdb/
当用了master选项之后,mongod会创建local.oplog.$main collection,
mongod --slave --source <masterhostname><:<port>> --dbpath /data/slavedb/
通过—source来指向master。
可以使用local.sources来指定一个master,不需要通过—source选项
1 use local
2 db.sources.find()
3 db.sources.insert( { host: <masterhostname><,only: databasename>} );
Host:host字段只想了一个master mongod实例,ip地址:port
Only:可选指定只会复制到指定的数据库
Master会把操作写入到oplog,如果slave被拉下,那么就需要重新同步,一下状况会被拉下:1.slave被master拉下太多,已经无法跟上。
2.因为slave停止,重启后发现已被master拉下。
当发现被拉下管理员需要手动启动重新同步,或者可以直接加参数—autoresync,为了防止出问题,可以把oplog的大小设置大一点默认只有磁盘可用空间5%的大小。
Master节点:master,salve
Slave节点:source,only,slaveDelay
通过db.printReplicationInfo()可以看master状态。使用db.printSlaveReplicationInfo()查看slave状态,使用db.serverStatus返回复制状态。
当启用auth,主从配置可以使用keyfile来做相互验证和交互。启用auth,设置keyfile指定keyfile文件,keyfile内容是随意的但是要一样。不可使用openssl来生成:
openssl rand -base64 741
如果要部署一个主从,可以用双节点复制集来代替。
{
_id : 'setName',
members : [
{ _id : 0, host : "<master>", priority : 1 },
{ _id : 1, host : "<slave>", priority : 0, votes : 0 }
]
}
重启当前master为单节点复制集
a.确定是否是master
b.干净关闭mongod,db.adminCommand({shutdown : 1, force : true})
c.备份数据
d.带—replSet重启mongod
e.初始化复制集,rs.initiate
使用rs.status来验证
1.关闭A
2.关闭b
3.把dbpath中所有的local文件都复制到b
4.重启b带—master选项。
1.使用fsync挂起写入
2.让b跟上A
3.关闭B
4.从dbpath上复制local到b上
5.使用—master启动b
6.做个写入,为了让oplog 有个起点
7.关闭b,有了local准备把local复制到a
8.关闭a,把b下的local复制到a
9.用master启动b
10.使用slave启动a,包括fastsync
若可以对master无限的停止时间,可以复制数据到slave,然后通过—fastsync启动。
Fastsync是通过备份或者磁盘镜像来启动slave的一种方式,由管理员保证image的正确性跟上master,如果有master的备份,就可以用这个选项避免启动slave时完全同步。
也可以通过slave数据文件的快照复制,只有在mongod停止或者使用db.fsynclock()才能获取快照。
Oplog是有限的,当被拉下的太多,就没办法通过oplog同步了,需要重新同步。
use admin
db.runCommand( { resync: 1 } )
强制同步所有数据,如果在大数据库下会非常慢。这个和你删除slave下的数据文件,然后重新同步效果一样。
Slave不能链,只能通过master同步。如果从其他slave同步就会报错。
通过修改local.source来修改slave的源。
1.重启不带—slave和—source
2.修改local.sourcecollection
use local
db.sources.update( { host : "prod.mississippi" },
{ $set : { host : "prod.mississippi.example.net" } } )
3.重启可以带—source,也可以不带。
管理复制集包含初始化复制集,增加删除成员,配置复制集,管理员不干预切换,自动切换个别状态要求手动干涉。
介绍,部署复制集,为测试和开发部署复制集,多地理位置部署复制集,添加仲裁,把standalone转化为复制集,增加成员到复制集,从复制集中删除成员,替换复制集中的成员。
介绍如何部署3节点复制集。如果想要从已有的数据库部署复制集,可以先把已有数据转化为复制集。
3节点复制集已经提供了足够多的冗余和问题。复制集应该保证奇数个成员。来保证投票能够顺利进行。
基本过程是配置一个复制集,然后加入成员。
尽量把不同的成员放到不同的主机上。再次之前先要在各个host上安装mongodb。并且保证成员之间可以相互通信。
1.mongodb打开27017端口
2.可以通过dns或者主机名访问成员
3.保证可以相互通信
4.在配置文件或者启动参数中指定复制集名称。
1.启动mongod指定一个复制集名,如果有配置文件写到配置文件上,如果没有可以用命令行参数。
2.连接到其中一个host
3.使用rs.initiate()初始化复制集
4.使用rs.conf()限制配置
{
"_id" : "rs0",
"version" : 4,
"members" : [{
"_id" : 1,
"host" : "mongodb0.example.net:27017"
}]
}
5.通过rs.add增加成员
rs.add("mongodb1.example.net")
rs.add("mongodb2.example.net")
完成后复制集会生产一个primary
3节点复制集已经提供了足够多的冗余和问题。复制集应该保证奇数个成员。来保证投票能够顺利进行。
基本过程是配置一个复制集,然后加入成员。
对于测试或开发环境,可以安装在同一个系统或者虚拟系统上。当然在部署前要先安装mongodb。之前保证成员间的网络状况。
1.创建数据文件夹
2.启动mongod,指定复制集名称,并指定端口
3.通过shell连接某个实例
4.使用rs.initiate()初始化复制集,也可以手动创建配置文件
5.显示配置文件。
rsconf = {_id: "rs0",
members: [{
_id: 0,
host: "<hostname>:27017"
}]
}
rs.initiate( rsconf )
6.添加节点,然后复制集自动选出primary
rs.add("<hostname>:27018")
rs.add("<hostname>:27019")
复制集对单个实例错误提供了基本的保护,但是如果成员都在一个数据中心,如果数据中心出错,那么就会受到影响。这样可以把成员放到其他地方来避免。
1.保证majority 投票成员在主数据中心。
2.若要部署仲裁放在主数据中心
若有2个成员在A,1个在B那么A应该很靠近你的主应用程序的设备或者就在A上。
对于4成员的集群至少要有2个在A上。
一般过程:
1.每个复制集成员在各自的设备上,所有Mongodb进程都使用27017端口。
2.每个成员都有可访问的DNS或者主机名
3.保证所有成员之间可以通信。
4.使用配置文件来进行配置。
多地理位置3节点复制集:
1.启动mongod –config /etc/mongodb.conf
2.打开mongo shell
3.使用rs.initiate来初始化复制集
4.rs.conf显示配置
5.rs.add增加节点
6.保证非主数据中心的优先级为0
cfg = rs.conf()
cfg.members[2].priority = 0
rs.reconfig(cfg)
注意点:rs.reconfig的时候会断开所有连接,并强制primarystep down,然后产生一个新的primary。
多地理位置4节点复制集:
1.必须加入一个仲裁,对设备没有要求
2.决定分布:
a.3个成员在A,一个成员在B优先级为0,1个仲裁在A
b.2个成员在A,2个成员在B优先级为0,1个仲裁在A
c.2个成员在A,1个成员在B优先级为0,1个成员在C优先级为0,1个仲裁在A
1.启动mongod带上config
2.打开mongo shell
3.rs.initate初始化
4.rs.conf显示配置
5.添加节点rs.add
6.增加仲裁rs.addArb
7.非主数据中心的优先级修改为0
cfg = rs.conf()
cfg.members[2].priority = 0
rs.reconfig(cfg)
多于4节点成员多地理位置部署:
大型复制集部署应该遵循以下几点:
1.不要部署超过7个投票成员
2.如果有偶数个成员,通过部署仲裁保证主数据中心有majority成员。
3.如果是计数个成员保证主数据中心有majority成员
仲裁的作用是在投票的时候产生大多数,打破对峙。仲裁对硬件要求很低。
1.创建仲裁的数据文件夹
2.启动仲裁服务–replSet
3.使用rs.addArb添加仲裁
1.关闭实例
2.带—replSet名启动
3.连接到实例
4.rs.initiate初始化
扩展复制集:
1.开启另外2个实例
2.rs.add添加节点
Shard考虑:
如果新建shard为复制集,需要在config数据库做一下操作:
1.连接到mongos然后运行一下命令
db.getSiblingDB("config").shards.save( {_id:"<name>", host:"<replica-set>/<member,><member,><...>" } )
2.重启所有mongos实例
最大投票成员:如果增加成员到已经有7个投票成员的复制集中,你要不添加不投票成员或者删除一个已经有投票的成员。
控制脚本:关于生成环境部署,可以配置控制脚本来管理成员进程
已存在成员:可以添加新的成员到已存在复制集
数据文件:如果有一个备份和快照,可以直接把数据文件移动到新的系统,然后快速的初始化新成员。文件要求:
1.从同一个复制成员中复制数据文件
2.最老的操作要必须在primary的oplog,新成员必须要跟上primary。
1.活动的复制集
2.一个有能力的mongodb实例,可以通过网络访问活动的复制集
准备数据文件夹策略:
1.保证数据文件夹下没有数据
2.手动从其他成员中复制数据
保证复制的文件在oplog窗口之内。
增加成员到已存在的复制集:
1.启动新的实例,指明—replSet
2.连接到复制集primary
3.使用rs.add增加成员
4.通过rs.conf查看成员。
配置和添加成员:
可以通过rs.add添加成员并配置。
rs.add({_id: 1, host: "mongodb3.example.net:27017", priority: 0, hidden: true})
1.关闭要删除的mongod
2.连接到当前primary,可以通过db.isMaster()查看
3.使用rs.remove删除成员
然后mongodb会产生一个primary。
rs.remove("mongod3.example.net:27017")
rs.remove("mongod3.example.net")
通过修改复制集配置文档手动的删除成员。
1.关闭要删除的mongod
2.连接到当前primary
3.rs.conf显示配置文档
4.获取配置文档 cfg=rs.conf
5.修改cfg来删除成员,cfg.members.splice(2,1)来删除第3个成员。
6.rs.reconfig(cfg)来重新配置配置文档,执行后产生primary,所有连接断开。
7.通过rs.conf验证。
如果需要修改复制集成员但是不改变成员的配置,可以如下使用
修改host字段,_id会在重新配置时被修改,rs.reconfig,任何复制集配置的修改会触发primary关闭,强制投票重新生成primary。在投票期间所有的连接都会被断开。
cfg = rs.conf()
cfg.members[0].host = "mongo2.example.net"
rs.reconfig(cfg)
介绍,合适的成员优先级,防止secondary变成primary,隐藏成员配置,延迟成员配置,非选举成员配置,secondary转化为仲裁
如下命令修改成员优先级:
cfg = rs.conf()
cfg.members[0].priority = 0.5
cfg.members[1].priority = 2
cfg.members[2].priority = 2
rs.reconfig(cfg)
先取出配置文档,修改配置文档,然后reconfig。
若优先级为0 就不能变成primary,隐藏,延迟,仲裁的优先级都为0.
优先级高的会有优先成为primary
把优先级设置为0就能防止secondary变成primary。
隐藏成员是复制集的一部分,不能成为primary,也不能被client访问。隐藏成员有投票权。
如果chainingAllowed设置允许secondary成员从其他secondary上同步数据。选择同步对象的时候mongodb更偏好从非隐藏成员同步。若要从隐藏成员同步,可以使用replSetSyncFrom。
{
"_id" : <num>
"host" : <hostname:port>,
"priority" : 0,
"hidden" : true
}
把hidden设置为true表示隐藏成员。
cfg = rs.conf()
cfg.members[0].priority = 0
cfg.members[0].hidden = true
rs.reconfig(cfg)
注意点:
1.rs.reconfig会强制当前primary step down,导致投票。当primary step down所有的client都会被关闭,会花10-20s的时间。
2.如果成员是偶数的要增加一个仲裁成员保证顺利投票产生primary。
延迟成员的优先级为0,并且为隐藏成员,通过slaveDelay来设置延迟时间。
cfg = rs.conf()
cfg.members[0].priority = 0
cfg.members[0].hidden = true
cfg.members[0].slaveDelay = 3600
rs.reconfig(cfg)
延迟成员不能成为primary,slaveDlay为延迟时间。
注意点:
1.rs.reconfig会强制当前primary step down,导致投票。当primary step down所有的client都会被关闭,会花10-20s的时间。
2.如果成员是偶数的要增加一个仲裁成员保证顺利投票产生primary。
非投票成员,当投票成员超过7个的时候,增加非投票成员来提高分布式读。
cfg = rs.conf()
cfg.members[3].votes = 0
cfg.members[4].votes = 0
cfg.members[5].votes = 0
rs.reconfig(cfg)
注意点:
1.rs.reconfig会强制当前primary step down,导致投票。当primary step down所有的client都会被关闭,会花10-20s的时间。
2.如果成员是偶数的要增加一个仲裁成员保证顺利投票产生primary。
通常,所有的成员都有1个票,防止了内部的对持,死锁,或不对的成员变成primary,通过使用优先级来控制是否能够成员primary。
如果secondary不需要保存数据,但是要保存投票,可以把secondary变成仲裁。可选:
1.仲裁运行在老的端口上
2.仲裁运行在新的端口上
1.如果应用程序可以连接到secondary,那么修改应用程序不能到达这个secondary
2.关闭secondary
3.rs.remove删除这个成员
4.查看配置rs.conf验证
5.移动数据文件
6.创建新的数据文件夹
7.重启实例
8.使用rs.addArb增加成员
9.rs.conf验证配置
1.如果应用程序可以连接到secondary,那么修改应用程序不能到达这个secondary
2.创建一个新的数据文件夹
3.启动指定新的端口
4.rs.addArb加入到复制集
5.rs.conf验证
6.关闭secondary
7.删除secondary,rs.remove
8.rs.conf验证
9.备份老的数据
修改oplog大小,强制成员变成primary,重新同步复制集,配置复制集tag,重新配置不可用成员,管理复制链,修改主机名,配置secondary同步tag
Oplog是一个capped collection,所以不能用一般的方法修改大小,一般情况下oplog的默认大小足够用了。
1.成员以standalone方式启动
2.用新的大小重新创建oplog
3.作为复制集成员重启
成员以standalone方式启动:
db.shutdownServer()
mongod --port 37017 --dbpath /srv/mongodb
备份oplog:
mongodump --db local --collection 'oplog.rs' --port 37017
为新的oplog备份:
use local
db.temp.save( db.oplog.rs.find( { }, { ts: 1, h: 1 } ).sort( {$natural : -1} ).limit(1).next() )
删除oplog:
db = db.getSiblingDB('local')
db.oplog.rs.drop()
创建新的oplog:
db.runCommand( { create: "oplog.rs", capped: true, size: (2 * 1024 * 1024 * 1024) } )
把备份的数据的最新数据插入到新的oplog:
db.oplog.rs.save( db.temp.findOne() )
重启成员:
db.shutdownServer()
mongod --replSet rs0 --dbpath /srv/mongodb
处理所有secondary,处理primary时先把primary变成secondary
配置如下:
{
"_id":"rs",
"version":7,
"members": [
{
"_id":0,
"host":"m1.example.net:27017"
},
{
"_id":1,
"host":"m2.example.net:27017"
},
{
"_id":2,
"host":"m3.example.net:27017"
}]
}
1.设置优先级
cfg = rs.conf()
cfg.members[0].priority = 0.5
cfg.members[1].priority = 0.5
cfg.members[2].priority = 1
rs.reconfig(cfg)
以下是发生的时间:
1.m3,m2从m1同步
2.m1优先级不是最高,如果m3同步被拉下很多m1不会step down,m1等待m3在10s之内,再step down
3.根据有限制设置,投票m3变成primary
2.可选,如果m3拉下了10s多,并且在这段时间内你在不需要primary,可以强制step down
db.adminCommand({replSetStepDown: 86400, force: 1})
86400表示在24小时之内防止m1再变成primary,如果等会儿反悔想让m1再变成primary使用rs.freeze允许m1可以变成primary。
Mdb0 primary,mdb1 secondary,mdb2 secondary
1.使用rs.status查看状态
2.在mdb2上使用rs.freeze(120),冻结120s,不让mdb2变成primary。
3.rs.stepDown(120)让primary,在120s不能成为primary。
如果成员被拉下太多已经超过了oplog的窗口,那么就需要重新同步。
2个重新同步方案:
1.以空的数据文件夹启动,让mongod初始化同步
2.使用其他成员的备份同步
1.如果已经存在一个成员,停止mongod,删除所有数据文件
2.启动mongod
这样mongod会初始化同步,初始化同步的时间取决于数据库的大小
初始化同步会影响其他成员,影响primary的吞吐量,可访问成员跟上primary。
复制数据文件:你可以通过快照或者直接复制来抓数据。一般不能直接复制,因为在复制过程中,有修改行为。不能使用mongodump只能用快照复制。
同步成员:可以使用备份的数据文件直接启动即可。
Tag可以用来客户化写注意和读偏好
读偏好考虑tag的值
写主义只看tag值是不是唯一的
conf = rs.conf()
conf.members[0].tags = { "dc": "east", "use": "production" }
conf.members[1].tags = { "dc": "east", "use": "reporting" }
conf.members[2].tags = { "use": "production" }
rs.reconfig(conf)
{
"_id":"rs0",
"version":2,
"members": [
{
"_id":0,
"host":"mongodb0.example.net:27017",
"tags": {
"dc":"east",
"use":"production"
}},
{
"_id":1,
"host":"mongodb1.example.net:27017",
"tags": {
"dc":"east",
"use":"reporting"
} },
{
"_id":2,
"host":"mongodb2.example.net:27017",
"tags": {
"use":"production"
}} ]
}
5个成员在2个数据中心,VA,GTO
1.创建复制集配置,conf = rs.conf()
2.增加tags
conf.members[0].tags = { "dc.va": "rack1"}
conf.members[1].tags = { "dc.va": "rack2"}
conf.members[2].tags = { "dc.gto": "rack1"}
conf.members[3].tags = { "dc.gto": "rack2"}
conf.members[4].tags = { "dc.va": "rack1"}
rs.reconfig(conf)
3.创建getLastErrorModes conf.settings = { getLastErrorModes: { MultipleDC : { "dc.va": 1, "dc.gto": 1}}
4.重新配置复制集rs.reconfig(conf)
这个写注意保证了至少写入到不同数据中心的一个成员。
db.runCommand( { getLastError: 1, w: "MultipleDC" } )
如果至少要写入不同数据中心的2个成员:
1.创建复制集配置,conf = rs.conf()
2.创建getLastErrorModes conf.settings = { getLastErrorModes: { MultipleDC : { "dc.va": 2, "dc.gto": 2}}
3.重新配置复制集rs.reconfig(conf)
{"dc.va": "rack1", disk:"ssd", ssd: "installed" }
{"dc.va": "rack2", disk:"raid"}
{"dc.gto": "rack1", disk:"ssd", ssd: "installed" }
{"dc.gto": "rack2", disk:"raid"}
{"dc.va": "rack1", disk:"ssd", ssd: "installed" }
1.创建复制集配置,conf = rs.conf()
2.创建getLastErrorModes
conf.settings = {
"getLastErrorModes": {
"ssd": {
"ssd":1
},
"MultipleDC": {
"dc.va":1,
"dc.gto":1
}
}
}
3.重新配置复制集rs.reconfig(conf)
db.runCommand( { getLastError: 1, w: "MultipleDC" } ),也可以使用ssd作为写注意。
这个方法可以在大多数成员不可用情况下恢复复制。Rs.reconfig的force选项一般只用来灾难性的中断,不是每次都这么用。不要在控制脚本上这么用,任然有primary也这么用。
1.备份留下成员
2.连接到留下来的成员复制当前的配置
3.参数已经不可用的成员只设置留下来的成员
cfg.members = [cfg.members[0] , cfg.members[4] , cfg.members[7]]
4.强制重新配置
rs.reconfig(cfg, {force : true})
5.如果问题只是展示的应该马上关掉被删除成员。
不再介绍因为只用于2.0之前的版本
复制链可以让secondary从另外一个secondary上同步。能够减少primary的压力,但会增加复制的延迟。
使用chainingAllowed来启动和管理复制链
1.创建复制集配置,conf = rs.conf()
2.若有settings就跳过,cfg.settings = { }
3.关闭复制链,重新配置
cfg.settings.chainingAllowed = false
rs.reconfig(cfg)
cfg = rs.config()
cfg.settings.chainingAllowed = true
rs.reconfig(cfg)
复制集中的主机名很少修改,不过也有可能在做迁移的时候需要修改。
提供了2种修改主机名的方法:
1.修改主机名但是不破坏可用性,这个方法保证了应用程序的可以从复制集上读写数据,但是这个方法会花比较多的时间,可能会增加应用程序的下线时间。要修改应用程序的配置。
2.关闭所有老成员,这个方法维护时间短,但是期间复制集不可用。
{
"_id":"rs",
"version":3,
"members": [
{
"_id":0,
"host":"database0.example.com:27017"
},
{
"_id":1,
"host":"database1.example.com:27017"
},
{
"_id":2,
"host":"database2.example.com:27017"
}
]
}
要修改为
mongodb0.example.net:27017 (the primary)
mongodb1.example.net:27017
mongodb2.example.net:27017
1.对于每个secondary
a.关闭实例
b.启动新位置的实例
c.使用mongo连接到primary
d.使用rs.reconfig来重新配置复制集
cfg = rs.conf()
cfg.members[1].host = "mongodb1.example.net:27017"
rs.reconfig(cfg)
e.保证客户端可以连接到新的secondary并且要跟上其他secondary
2.step down,让primary变成secondary,和处理secondary一样处理。
3.rs.conf验证
1.关闭所有成员
2.使用别的端口启动
3.对于每个成员:
a.mongo shell连接到成员
b.编辑配置文件
use local
cfg = db.system.replset.findOne( { "_id": "rs" } )
cfg.members[0].host = "mongodb0.example.net:27017"
cfg.members[1].host = "mongodb1.example.net:27017"
cfg.members[2].host = "mongodb2.example.net:27017"
db.system.replset.update( { "_id": "rs" } , cfg )
c.关闭mongod
4.启动每个成员并设置上—replSet
5.mongo shell连接
6.rs.confg验证
可以使用rs.syncFrom()或者replSetSyncFrom命令来覆盖默认的同步对象。在初始化同步之前运行rs.syncFrom会影响初始化同步对象,如果已经在初始化了,再运行对象不会有变化。
Rs.syncFrom只是临时的修改默认行为,有这些状况下,还是会从默认的同步对象初始化:
1.mongod重启了
2.mongod和同步对象的连接被关闭。
当目标对象落后30s以上,会从默认同步对象同步。
使用rs.status()来检查复制集和当前成员的状态。
复制延迟是primary和secondary应用这个操作的oplog的时间差。延迟严重影响复制集的部署。
检查当前的延迟:
1.连接到primary调用db.printSlaveReplicationInfo(),返回syncedTo的值:
source: m1.example.net:30001
syncedTo: Tue Oct 02 2012 11:33:40 GMT-0400 (EDT)
= 7475 secs ago (2.08hrs)
source: m2.example.net:30002
syncedTo: Tue Oct 02 2012 11:33:40 GMT-0400 (EDT)
= 7475 secs ago (2.08hrs)
2.使用MMS来监控复制
可能照成延迟的原因:
网络延迟:保证成员之间的网络,是否存在丢包现象
磁盘吞吐量:如secondary上的磁盘速度不够快,导致secondary不能及时跟上。
并发:长时间运行的操作会堵塞在secondary上的复制,最好的办法配置写注意来保证写入到secondary
合适的写注意:当有大量的写入但是没有写注意可能会导致secondary无法跟上primary。为了防止这种情况,每100或者1000或者指定的间隔,使用写通知或者journal,让secondary能够跟上primary。
每个成员之间需要相互通信,不然会影响复制集。可以使用mongo shell是否能够连接上来看成员间是否可以通信。
当重启多个成员的时候,要保证复制集还是可以选取一个primary,也就是说大多数的vote成员可用。
当活动的成员不能成为大多数,primary step down变成secondary,没有成员变成primary。之前的primary关闭所有连接,导致写入异常。
大的oplog可以容忍复制集更大的延迟。
可以使用db.printReplicationInfo()输出oplog大小:
configured oplog size: 10.10546875MB
log length start to end: 94400 (26.22hrs)
oplog first event time: Mon Mar 19 2012 13:50:38 GMT-0400 (EDT)
oplog last event time: Wed Oct 03 2012 14:59:10 GMT-0400 (EDT)
now: Wed Oct 03 2012 15:00:21 GMT-0400 (EDT)
oplog的大小确定了复制集的最大延迟。一般至少要保证oplog24小时的延迟。
replSet error fatal couldn't query the local local.oplog.rs collection. Terminating mongod after 30 <timestamp> [rsStart] bad replSet oplog entry?
出现上面错误时,一般认为是时间戳错误。通过以下查询检查ts字段的类型是否真的有问题。
db = db.getSiblingDB("local")
db.oplog.rs.find().sort({$natural:-1}).limit(1)
db.oplog.rs.find({ts:{$type:17}}).sort({$natural:-1}).limit(1)
时间戳的类型在BSON为17,如果2个查询返回不同的文档说明时间戳类型错误。
通过以下操作来纠正类型错误:
db.oplog.rs.update( { ts: { t:1347982456000, i:1 } },
{ $set: { ts: new Timestamp(1347982456000, 1)}})
这个操作可能要花点时间因为会扫描所有的文档。
local.slaves的重复键错误,这个错误一般会出现在secondary的主机名修改,然后primary试图修改local.slaves的时候出现。出现错误是因为保存了同一个_id。错误如下:
exception 11000 E11000 duplicate key error index: local.slaves.$_id_ dup key: { : ObjectId('<object ID>') } 0ms
这个操作不会影响secondary上的复制。为了防止这个问题可以删除primary上的local.servers
use local
db.slaves.drop()
然后secondary会把这个拉给primary,然后primary重建local.slaves即可。
名称 |
描述 |
增加成员 |
|
增加仲裁到复制集. |
|
查看复制集配置. |
|
指定时间内,阻止当前节点变成primary |
|
复制集函数帮助 |
|
初始化复制集 |
|
重新配置复制集 |
|
删除复制集的一个成员 |
|
设置当前连接的slaveOK,已弃用,使用readPref()或者Mongo.setReadPref()来设置读偏好 |
|
返回当前复制集的状态 |
|
把primary变成secondary并强制以后一场投票 |
|
配置当前成员的syncFrom,覆盖默认的syncFrom |
Name |
Description |
指定时间内,防止当前成员变成primary |
|
返回当前复制集状态 |
|
初始化新复制集 |
|
启动会在关闭维护模式,会让secondary变成RECONVERING状态 |
|
应用新的复制集设置. |
|
强制当前primary step down变成secondary,强制一场投票 |
|
指定默认的syncfrom并覆盖默认syncfrom |
|
重新从master同步,只应用于主从复制 |
|
内部命令应用oplog到当前数据集 |
|
显示成员角色信息 |
|
内部命令,返回optime |
参考文档p467,在2.4.9中已经把reference放到了独立一章
rs.conf来获取复制集的配置
{
_id:<setname>,
version:<int>,
members:[
{
_id:<ordinal>,
host:hostname<:port>,
<arbiterOnly:<boolean>,>
<buildIndexes:<boolean>,>
<hidden:<boolean>,>
<priority:<priority>,>
<tags:{<document>},>
<slaveDelay:<number>,>
<votes:<number>>
}
,...
],
<settings:{
<getLastErrorDefaults:<lasterrdefaults>,>
<chainingAllowed:<boolean>,>
<getLastErrorModes:<modes>>
}>
}
http://docs.mongodb.org/manual/reference/replica-configuration/
每个mongod都有自己的local数据库,保存了复制过程,和其他实例特殊数据,local数据库对复制不可见。
在复制中,local数据库为每个成员保存了内部复制数据。
具体请看:http://docs.mongodb.org/manual/reference/local-database/
local.Statuplog
在启动的时候每个mongod实例插入一个文档到startup_log用来诊断
{
"_id":"<string>",
"hostname":"<string>",
"startTime":ISODate("<date>"),
"startTimeLocal":"<string>",
"cmdLine":{
"dbpath":"<path>",
"<option>":<value>
},
"pid":<number>,
"buildinfo":{
"version":"<string>",
"gitVersion":"<string>",
"sysInfo":"<string>",
"loaderFlags":"<string>",
"compilerFlags":"<string>",
"allocator":"<string>",
"versionArray":[<num>,<num>,<...>],
"javascriptEngine":"<string>",
"bits":<number>,
"debug":<boolean>,
"maxBsonObjectSize":<number>
}
}
local.system.replset:保存了复制集配置对象
local.oplog.rs:保存了oplog
local.replset.minvalid:包含了一个说明复制集跟踪状态的一个对象内部使用
local.slaves:包含了复制集的每个成员,最后的同步时间
local.oplog.$main:主从复制的oplog
local.slaves:每个slave信息
local.sources:master的信息
详细请看:http://docs.mongodb.org/manual/reference/replica-states/
Number |
Name |
State Description |
0 |
STARTUP |
不能vote,所有成员从这个状态开始,在这个状态时解析复制集配置文档 |
1 |
PRIMARY |
可以vote,复制集同一时间只能有一个primary支持读写 |
2 |
SECONDARY |
可以vote,保存冗余数据 |
3 |
RECOVERING |
可以vote,成员在执行自我检查,回滚或者resync时在这个状态 |
4 |
FATAL |
不能vote,发生了一个不可恢复的错误 |
5 |
STARTUP2 |
不能vote,在变成secondary之前fork复制和投票线程 |
6 |
UNKNOWN |
不能vote,不能连接到成员 |
7 |
ARBITER |
可以vote,仲裁成员不保存数据只能投票 |
8 |
DOWN |
不能vote,不可访问 |
9 |
ROLLBACK |
可以vote,执行回滚中 |
10 |
SHUNNED |
不能vote,是否被复制集删除 |
http://docs.mongodb.org/manual/reference/read-preference/
Read Preference Mode |
Description |
primary |
默认方式,只从primary上读取 |
primaryPreferred |
先读从primary读,不行从secondary上读 |
secondary |
从secondary上读 |
secondaryPreferred |
先从secondary上读,不行从primary上读 |
nearest |
从最近的一个节点上读 |