刚接触MongoDB,就要用到它的集群,只能硬着头皮短时间去看文档和尝试自行搭建。迁移历史数据更是让人恼火,近100G的数据文件,导入、清理垃圾数据执行的速度蜗牛一样的慢。趁着这个时间,把这几天关于Mongod集群相关的内容整理一下。大概介绍一下MongoDB集群的几种方式:Master-Slave、Relica Set、Sharding,并做简单的演示。
使用集群的目的就是提高可用性。高可用性H.A.(High Availability)指的是通过尽量缩短因日常维护操作(计划)和突发的系统崩溃(非计划)所导致的停机时间,以提高系统和应用的可用性。它与被认为是不间断操作的容错技术有所不同。HA系统是目前企业防止核心计算机系统因故障停机的最有效手段。
HA的三种工作方式:
-
主从方式 (非对称方式)
工作原理:主机工作,备机处于监控准备状况;当主机宕机时,备机接管主机的一切工作,待主机恢复正常后,按使用者的设定以自动或手动方式将服务切换到主机上运行,数据的一致性通过共享存储系统解决。
-
双机双工方式(互备互援)
工作原理:两台主机同时运行各自的服务工作且相互监测情况,当任一台主机宕机时,另一台主机立即接管它的一切工作,保证工作实时,应用服务系统的关键数据存放在共享存储系统中。
-
集群工作方式(多服务器互备方式)
工作原理:多台主机一起工作,各自运行一个或几个服务,各为服务定义一个或多个备用主机,当某个主机故障时,运行在其上的服务就可以被其它主机接管
主从架构(Master-Slave)
Mater-Slaves
主从架构一般用于备份或者做读写分离。由两种角色构成:
-
主(Master)
可读可写,当数据有修改的时候,会将oplog同步到所有连接的salve上去。
-
从(Slave)
只读不可写,自动从Master同步数据。
特别的,对于Mongodb来说,并不推荐使用Master-Slave架构,因为Master-Slave其中Master宕机后不能自动恢复,推荐使用Replica Set,后面会有介绍,除非Replica的节点数超过50,才需要使用Master-Slave架构,正常情况是不可能用那么多节点的。
还有一点,Master-Slave不支持链式结构,Slave只能直接连接Master。Redis的Master-Slave支持链式结构,Slave可以连接Slave,成为Slave的Slave。
下面演示一下搭建过程:
1>. 启动Master
mongod --port 2000 --master --dbpath masterdb/
2>. 启动Slave
mongod --port 2001 --slave --source 127.0.0.1:2000 --dbpath slavedb/
3>. 给Master里面导入数据,查看Master和Slave的数据。你会发现导入Master的数据同时也会在Slave中出现。
mongoimport --port 2000 -d test -c dataset dataset.json
mongo --port 2000 test
db.dataset.count()
> 25359
mongo --port 2001 test
db.dataset.count()
> 25359
4>. 试一下Master和Slave的写操作。你会发现,只有Master才可以对数据进行修改,Slave修改时候会报错。
mongo --port 2001 test
db.dataset.drop()
> Error: drop failed: { "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" }
mongoimport --port 2001 -d test -c dataset dataset.json
> Failed: error checking connected node type: no reachable servers
副本集架构(Replica Set)
为了防止单点故障就需要引副本(Replication),当发生硬件故障或者其它原因造成的宕机时,可以使用副本进行恢复,最好能够自动的故障转移(failover)。有时引入副本是为了读写分离,将读的请求分流到副本上,减轻主(Primary)的读压力。而Mongodb的Replica Set都能满足这些要求。
Replica Set的一堆mongod
的实例集合,它们有着同样的数据内容。包含三类角色:
-
主节点(Primary)
接收所有的写请求,然后把修改同步到所有Secondary。一个Replica Set只能有一个Primary节点,当Primar挂掉后,其他Secondary或者Arbiter节点会重新选举出来一个主节点。默认读请求也是发到Primary节点处理的,需要转发到Secondary需要客户端修改一下连接配置。
-
副本节点(Secondary)
与主节点保持同样的数据集。当主节点挂掉的时候,参与选主。
-
仲裁者(Arbiter)
不保有数据,不参与选主,只进行选主投票。使用Arbiter可以减轻数据存储的硬件需求,Arbiter跑起来几乎没什么大的硬件资源需求,但重要的一点是,在生产环境下它和其他数据节点不要部署在同一台机器上。
注意,一个自动failover的Replica Set节点数必须为奇数,目的是选主投票的时候要有一个大多数才能进行选主决策。
应用客户端
客户端连接单个mongod
和副本集的操作是相同,只需要配置好连接选项即可,比如下面是node.js连接Replica Set的方式:
mongoose.connect('mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]' [, options]);
Primary和Secondary搭建的Replica Set
Primary和Secondary搭建的Replica Set
奇数个数据节点构成的Replica Set,下面演示精典的3个数据节点的搭建过程。
1> 启动3个数据节点,--relSet
指定同一个副本集的名字
mongod --port 2001 --dbpath rs0-1 --replSet rs0
mongod --port 2002 --dbpath rs0-2 --replSet rs0
mongod --port 2003 --dbpath rs0-3 --replSet rs0
2> 连接到其中一个,配置Replica Set,同时正在执行rs.add
的节点被选为Primary。开发环境中<hostname>
指的是机器名,生产环境下就是机器的IP。
mongo --port 2001
rs.initiate()
rs.add("<hostname>:2002")
rs.add("<hostname>:2003")
rs.conf()
3> 连接Primary节点,导入数据成功。
mongoimport --port 2001 -d test -c dataset dataset.json
mongo --port 2001 test
db.dataset.count()
> 25359
4> 默认情况下,Secondary不能读和写。
mongo --port 2003 test
db.dataset.count()
> Error: count failed: { "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" }
注意,其中Secondary宕机,不受影响,若Primary宕机,会进行重新选主:
自动Failover
使用Arbiter搭建Replica Set
偶数个数据节点,加一个Arbiter构成的Replica Set,下面演示精典的2个数据节点加一个仲裁者的搭建过程。
特别的,生产环境中的Arbiter节点,需要修改一下配置:
journal.enabled = false
smallFiles = true
使用Arbiter搭建Replica Set
1> 启动两个数据节点和一个Arbiter节点
mongod --port 2001 --dbpath rs0-1 --replSet rs0
mongod --port 2002 --dbpath rs0-2 --replSet rs0
mongod --port 2003 --dbpath arb --replSet rs0
2> 连接到其中一个,添加Secondary和Arbiter。当仅需要添加Aribiter的时候,只需连接当前Replica Set的Primary,然后执行rs.addArb
。
mongo --port 2001
rs.initiate()
rs.add("<hostname>:2002")
rs.addArb("<hostname>:2003")
rs.conf()
数据分片架构(Sharding)
当数据量比较大的时候,我们需要把数据分片运行在不同的机器中,以降低CPU、内存和IO的压力,Sharding就是这样的技术。数据库主要由两种方式做Sharding:纵向,横向,纵向的方式就是添加更多的CPU,内存,磁盘空间等。横向就是上面说的方式,如图所示:
MongoDB的Sharding架构:
MongoDB的Sharding架构
MongoDB分片架构中的角色:
-
数据分片(Shards)
保存数据,保证数据的高可用性和一致性。可以是一个单独的
mongod
实例,也可以是一个副本集。在生产环境下Shard是一个Replica Set,以防止该数据片的单点故障。所有Shard中有一个PrimaryShard,里面包含未进行划分的数据集合:
-
查询路由(Query Routers)
mongos
的实例,客户端直接连接mongos
,由mongos
把读写请求路由到指定的Shard上去。一个Sharding集群,可以有一个mongos
,也可以有多mongos
以减轻客户端请求的压力。
-
配置服务器(Config servers)
保存集群的元数据(metadata),包含各个Shard的路由规则。
搭建一个有2个shard的集群
1> 启动两个数据分片节点。在此仅演示单个mongod
的方式,Replica Set类似。
mongod --port 2001 --shardsvr --dbpath shard1/
mongod --port 2002 --shardsvr --dbpath shard2/
2> 启动配置服务器
mongod --port 3001 --dbpath cfg1/
mongod --port 3002 --dbpath cfg2/
mongod --port 3003 --dbpath cfg3/
3> 启动查询路由mongos
服务器
mongos --port 5000 --configdb 127.0.0.1:3001,127.0.0.1:3002,127.0.0.1:3003
4> 连接mongos,为集群添加数据分片节点。
mongo --port 5000 amdmin
sh.addShard("127.0.0.1:2001")
sh.addShard("127.0.0.1:2002")
如果Shard是Replica Set,添加Shard的命令:
sh.addShard("rsname/host1:port,host2:port,...")
rsname - 副本集的名字
5> 可以连接mongos
进行数据操作了。
mongo --port 5000 test
mongoimport.exe --port 5000 -d test dataset.json
> 25359
数据的备份和恢复
MongodDB的备份有多种方式,这里只简单介绍一下mongodump
和mongorestore
的用法。
1> 备份和恢复所有db
mongodump -h IP --port PORT -o BACKUPPATH
mongorestore -h IP --port PORT BACKUPPATH
2> 备份和恢复指定db
mongodump -h IP --port PORT -d DBNAME -o BACKUPPATH
mongorestore -h IP --port PORT -d DBNAME BACKUPPATH
mongorestore -h IP --port PORT --drop -d DBNAME BACKUPPATH
3> 备份和恢复指定collection
mongodump -h IP --port PORT -d DBNAME -c COLLECTION -o xxx.bson
mongorestore -h IP --port PORT -d DBNAME -c COLLECTION xxx.bson
mongorestore -h IP --port PORT --drop -d DBNAME -c COLLECTION xxx.bson
小结
MongoDB的集群能力还是很强的,搭建还算是简单。最关键的是要明白上面提到的3种架构的原理,才能用的得心应手。当然不限于MongoDB,或许其他数据库也多多少少支持类似的架构。
https://www.jianshu.com/p/2825a66d6aed
https://blog.csdn.net/lncdzh/article/details/79568660
MongoDB集群运维笔记
前面的文章介绍了MongoDB副本集和分片集群的做法,下面对MongoDB集群的日常维护操作进行小总结:
MongDB副本集故障转移功能得益于它的选举机制。选举机制采用了Bully算法,可以很方便从分布式节点中选出主节点。Bully算法是一种协调者(主节点)竞选算法,主要思想是集群的每个成员都可以声明它是主节点并通知其他节点。别的节点可以选择接受这个声称或是拒绝并进入主节点竞争。被其他所有节点接受的节点才能成为主节点。节点按照一些属性来判断谁应该胜出。这个属性可以是一个静态ID,也可以是更新的度量像最近一次事务ID(最新的节点会胜出)。
1)MongoDB集群的节点数量
官方推荐MongoDB副本集的成员数量最好为奇数,且选举要求参与的节点数量必须大于成员数的一半。假设MongoDB集群有3个节点,那么只要有2个节点活着就可以选举;如果有5个,那么活3个节点就可以选举;如果有7个节点,那么活4个就可以选举.....
MongoDB集群最多允许12个副本集节点,其中最多7个节点参与选举。这是为了减少心跳请求的网络流量和选举话费的时间,心跳每2秒发送一次。
MongoDB集群最多12个副本集节点,是因为没必要一份数据复制那么多份,备份太多反而增加了网络负载和拖慢了集群性能;而最多7个节点参与选举是因为内部选举机制
节点数量太多就会导致1分钟内还选不出主节点,凡事只要适当就好。
2)MongoDB心跳
整个MongoDB集群需要保持一定的通信才能知道哪些节点活着哪些节点挂掉。MongoDB节点会向副本集中的其他节点每两秒就会发送一次pings包,如果其他节点在10秒
钟之内没有返回就标示为不能访问。每个节点内部都会维护一个状态映射表,表明当前每个节点是什么角色、日志时间戳等关键信息。如果是主节点,除了维护映射
表外还需要检查自己能否和集群中内大部分节点通讯,如果不能则把自己降级为secondary只读节点。
3)MongoDB同步
MongoDB副本集同步分为初始化同步和keep复制。初始化同步指全量从主节点同步数据,如果主节点数据量比较大同步时间会比较长。而keep复制指初始化同步过后,
节点之间的实时同步一般是增量同步。初始化同步不只是在第一次才会被处罚,有以下两种情况会触发:
[1] secondary第一次加入,这个是肯定的。
[2] secondary落后的数据量超过了oplog的大小,这样也会被全量复制。
=============================================================================================
何为oplog?
oplog(应用日志)保存了数据的操作记录,oplog主要用于副本,secondary复制oplog并把里面的操作在secondary执行一遍。但是oplog也是mongodb的一个集合,保存在local.oplog.rs里;然而这个oplog是一个capped collection,也就是固定大小的集合,新数据加入超过集合的大小会覆盖,所以这里需要注意,跨IDC的复制要设置合适的oplogSize,避免在生产环境经常产生全量复制。oplogSize可以通过--oplogSize设置大小,对于Linux 和Windows 64位,oplog size默认为剩余磁盘空间的5%。
在mongodb主从结构中,主节点的操作记录成为oplog(operation log)。oplog存储在一个系统数据库local的集合oplog.$main中,这个集合的每个文档都代表主节点上执行的一个操作。从服务器会定期从主服务器中获取oplog记录,然后在本机上执行!对于存储oplog的集合,MongoDB采用的是固定集合,也就是说随着操作过多,新的操作会覆盖旧的操作!主节点通过--oplogSize设置oplog的大小(主节点操作记录存储到local的oplog中)
=============================================================================================
MongoDB同步也并非只能从主节点同步,假设集群中3个节点,节点1是主节点在IDC1,节点2、节点3在IDC2,初始化节点2、节点3会从节点1同步数据。后面节点2、节点3会使用就近原则从当前IDC的副本集中进行复制,只要有一个节点从IDC1的节点1复制数据。设置mongodb、同步还要注意以下几点:
[1] secondary不会从delayed(延迟)和hidden(隐藏)成员上复制数据。
[2] 要是需要同步,两个成员的buildindexes必须要相同无论是否是true和false。
[3] buildindexes主要用来设置是否这个节点的数据用于查询,默认为true。
[4] 如果同步操作30秒都没有反应,则会重新选择一个节点进行同步。
4)Mongodb主节点的读写压力过大如何解决?
在系统早期,数据量还小的时候不会引起太大的问题,但是随着数据量持续增多,后续迟早会出现一台机器硬件瓶颈问题的。而MongoDB主打的就是海量数据架构,它不能解决海量数据怎么行! mongodb的"分片"就是用来解决这个问题的。
传统数据库怎么做海量数据读写?其实一句话概括:分而治之。如下TaoBao早期的一个架构图:
上图中有个TDDL,是TaoBao的一个数据访问层组件,它主要的作用是SQL解析、路由处理。根据应用的请求的功能解析当前访问的sql判断是在哪个业务数据库、哪个表访问查询并返回数据结果。具体如图:
说了这么多传统数据库的架构,那NoSQL怎么去做到了这些呢?MySQL要做到自动扩展需要加一个数据访问层用程序去扩展,数据库的增加、删除、备份还需要程序去控制。一但数据库的节点一多,要维护起来也是非常头疼的。不过MongoDB所有的这一切通过它自己的内部机制就可以搞定的了。如下图看看MongoDB通过哪些机制实现路由、分片:
从图中可以看到有四个组件:mongos、config server、shard、replica set。
mongos,数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加一个路由选择器,mongos自己就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境通常有多mongos作为请求的入口,防止其中一个挂掉所有的mongodb请求都没有办法操作。
config server,顾名思义为配置服务器,存储所有数据库元信息(路由、分片)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从 config server 加载配置信息,以后如果配置服务器信息变化会通知到所有的 mongos 更新自己的状态,这样 mongos 就能继续准确路由。在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,这个可不能丢失!就算挂掉其中一台,只要还有存货, mongodb集群就不会挂掉。
shard,这就是传说中的分片了。上面提到一个机器就算能力再大也有天花板,就像军队打仗一样,一个人再厉害喝血瓶也拼不过对方的一个师。俗话说三个臭皮匠顶个诸葛亮,这个时候团队的力量就凸显出来了。在互联网也是这样,一台普通的机器做不了的多台机器来做,如下图:
一台机器的一个数据表 Collection1 存储了 1T 数据,压力太大了!在分给4个机器后,每个机器都是256G,则分摊了集中在一台机器的压力。也许有人问一台机器硬盘加大一点不就可以了,为什么要分给四台机器呢?不要光想到存储空间,实际运行的数据库还有硬盘的读写、网络的IO、CPU和内存的瓶颈。在mongodb集群只要设置好了分片规则,通过mongos操作数据库就能自动把对应的数据操作请求转发到对应的分片机器上。在生产环境中分片的片键可要好好设置,这个影响到了怎么把数据均匀分到多个分片机器上,不要出现其中一台机器分了1T,其他机器没有分到的情况,这样还不如不分片!
replica set(副本集),其实上图4个分片如果没有 replica set 是个不完整架构,假设其中的一个分片挂掉那四分之一的数据就丢失了,所以在高可用性的分片架构还需要对于每一个分片构建 replica set 副本集保证分片的可靠性。生产环境通常是 2个副本 + 1个仲裁(即一主一从一仲裁)。
5)MongoDB 复制集节点增加移除及节点属性配置
复制集(replica Set)或者副本集是MongoDB的核心高可用特性之一,它基于主节点的oplog日志持续传送到辅助节点,并重放得以实现主从节点一致。再结合心跳机制,当感知到主节点不可访问或宕机的情形下,辅助节点通过选举机制来从剩余的辅助节点中推选一个新的主节点从而实现自动切换。对于一个已经存在的MongoDB Replica Set集群,可以对其进行节点的增加,删除,以及修改节点属性等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
6)MongoDB复制集状态查看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
7)MongoDB复制集常用监控工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
1 2 3 4 5 6 |
|
8)对于Secondary来说有几个比较重要的属性:Priority优先级、Vote投票节点、Hidden节点、Delayed节点
设定节点的优先级别(Priority)
Priority=0 即优先级为0的节点。字面上来说,权限为0。拥有最低的权限。已然是Secondary了,权限还最低,啥影响呢?之前说过,mongoDB的副本集中是有投票机制的,如果一个Primary不可达,那么所有的Secondary会联合起来投票选举,选出心目中的新的Primary。因为只有Primary才能接收Writes的操作,所以Primary在一个mongoDB的集群中是必须的。下图展示了一个在两个IDC中存放Primary,Secondary,以及一个Priority=0的Secondary的场景(关于这个存放方式以及奇数偶数。
优先级为0的节点的特点
1 2 3 4 |
|
Priority=0在mongoDB中的解释就是一个Standby,可投票不可参选,又干活又负载。有点像日本议会党派中刚入党派的小喽啰,可以参与自己党派党首的选举,还要干好多活,外面民众开骂还得挡着,但就是不可能当选党首。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
投票节点(Vote)
1 2 3 |
|
隐藏节点(Hidden)
字面上来说,隐藏。这个隐藏式对客户端的隐藏,客户端如果要读取Secondary的数据,永远无法读取Hidden节点的数据,因为设置了Hidden的这个节点对于客户端是透明的,不可见。但是,对于自己的Secondary的群体和Primary来说都是可见的,所以,Hidden依然可以投票,依然要按照oplog进行命令的复制,只是,不参与负载了。Hidden属性的前提是必须是一个Priority=0的节点,所以会具备一些优先级=0的特点,具体如下。
Hidden节点的特点
1 2 3 4 5 |
|
第3条特征体现出它与Priority=0的不同地方,第4条特征表现出它比0优先级多出来的特性。如果节点是Hidden,它不参与负载,那么空闲出来的时间可以做一些赋予给它的特殊任务,比如数据备份等等。到应用场景的时候会有用处。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
延迟节点(Delayed)
字面上来说,迟延。迟延代表此节点的数据与Primary的数据有一定的迟延,通过设定一个迟延的属性来确定。假设,Primary的数据是10:00的最新数据,我们设置了一个3600秒的迟延参数,那么这个带有迟延的节点的数据或者说命令执行情况(在oplog中)应该只到9:00为止。与主节点有1小时的迟延。有些人可能会问,我们设计分布式数据库要的就是数据能够尽量避免迟延来达到一致,这样才能更好的提供服务,为什么要刻意制造迟延呢?试想这个场景:一个猪一样的队友在mongoDB的Replica集群上面执行了一个Drop操作,这个操作干掉了你的Primary的Collection,这个Drop同时被记录到oplog中去,其他的Secondary看到这个oplog后争相执行,各自干掉了自己的Collection,你苦心存储的数据就这么消失了。。。再怎么抽这个队友没用啊。所以,主动的过失避免就显得格外重要。如果你有一个Delayed节点,有一个1000秒的迟延,那么在你发现这个miss之后还有足够的时间可以响应去不让这个Delayed节点执行错误的command,从而挽回你的损失。具体如下。
Delayed节点的特点
1 2 |
|
Delayed节点的最大作用是用来容人为的灾,猪一样的操作,驴一样的动作,在Delayed节点可以把损失降到最低。当然,如果你在Delayed时间经过后发现了错误,那么只能"呵呵"了。Delayed的时间设定一定要大于响应时间,比如从Primary的oplog写到Secondary需要1秒,那么Delayed必须大于等于1秒,小于1秒的的话只能是一个不可及状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
mongodb副本集修改配置问题
1 2 3 4 5 6 7 8 |
|
9)其他维护说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
|