【MongDB】MongDB企业级高可用集群搭建 复制集 数据分片

前言:随着用户数量的膨胀,大多数应用的瓶颈往往都会出现在数据库上,MongDB虽然性能极高,但在面对海量数据时,单机版的架构依旧显得无力,垂直扩展成本较高,还好MongDB比较友好,支持横向扩展,MongDB支持数据分片,有用过Mycat的可以很好理解,其内置了类似于Mycat的分库分表的功能,相当于是Mysql+Mycat.网上关于集群搭建的教程也是颇多,但大部分是伪集群,另外MongDB版本迭代极快,如果你使用了最新的版本,比如4.X,而教程往往停留在2.X 3.X这种版本,很可能会有一些未知的坑,让人很难爬出来,浪费不必要的时间,顺便喷一下那些随意转别人博客都不亲自测试下的人,有些简直是误人子弟! 希望本篇可以帮到一些喜欢新版本新技术的人,所有过程本人亲测有效,遇到的所有坑和解决办法也会在文末提到.

相较于Redis集群,Mysql集群,个人感觉MongDB集群搭建起来难度要更大,涉及的知识点和内容也最多.

在正式搭建开始前,先再次梳理下MongDB集群的一些相关内容,这样可以更好的帮助我们搭建和理解MongDB集群.

 

1.MongDB的三种集群方式

1.1Master/Slave

Master/Slave这种方式基本上不再推荐使用,只能从Master复制数据到Slave,并不提供高可用,一旦Master结点出故障就比较难处理。具体细节就不说了,反正已经不推荐使用。

1.2Replica Set

即常说的复制集,复制集的主要目标有几个:

  • 高可用(主要目标):当一个结点故障时自动切换到其他结点;
  • 数据冗余(主要目标):数据复制到n个结点上,增加数据安全性,同时为高可用提供基础;
  • 功能隔离(次要目标):使用不同的结点隔离某些有特殊需求的功能,比如使用一个结点进行OLAP运算(大规模资源占用),使用一个结点在远程做灾备(性能要求不如本地高),读写分离等等;

这种方式搭建起来相对容易,可靠性等各方面表现都不错,是用的比较多的一种方式.

 

1.3Sharded Cluster

即分片集,分片集的主要设计目标是:

  • 水平扩展:当一台服务器满足不了需求的时候,我们可以选择垂直扩展(增加服务器硬件),它虽然简单,但很容易达到极限,并且面临成本高等明显缺点。成本更低的方式是使用n台服务器组成集群来满足系统需求。这就是分片集的主要设计目标;
  • 缩短响应时间:因为可以把数据分散到多台服务器上,自然每台服务器的处理压力减小,处理时间就会缩短;

这里会出现一个问题:假设每台服务器出故障的机率是x%,那么n台服务器有一台出现故障的机率就是x% * n,如果不做高可用设计,集群出现故障的概率就会随机器数量成正比增长,这在工程上是不能接受的。幸运的是我们已经有了解决高可用的方案,也就是复制集。所以MongoDB的分片集群要求每一个片都是复制集(当然测试环境也可以使用单结点,生产环境不推荐).

在面对海量数据时,这种方式表现更加游刃有余.

Replica Set 主要是为了数据的高可用性,有点类似于Mysql的主从复制,而Sharded Cluster就相当于是Mysql的分库分表,为了实现高可用性和高性能,本篇就主要讲复制集+分片集结合的方式,挑战最高难度!

 

2.MongDB的复制和分片

2.1MongDB的复制成员

前面提到,MongDB的Replica set 复制集 主要由三个成员组成:primary,secondary,arbiter,如图所示:

其中primary是主节点,secondary是从节点,arbiter是仲裁者,一个集群只能有一个主节点,当主节点挂了以后,复制集会自动投票选出一个从节点作为新的主节点,当挂了的主节点恢复正常以后,挂了的主节点会变为从节点加入该集群中,跟zookeeper一样.

仲裁者的主要作用就是投票,它不参与数据的复制等,仅占用系统较少的资源.

当然你也可以采用下面这种没有arbiter仲裁者的集群架构:

 

当你的系统节点数量为2n+1时,你可以采用这种架构,当然n必须大于1,否则仅有3个节点时,当主挂了剩下2个节点无法投出奇数个票,所以如果你的集群仅有3个节点,是需要仲裁者的,也比较推荐第一种有仲裁者的方式.

 

2.2MongoDB的分片集群成员

Diagram of a sample sharded cluster for production purposes.  Contains exactly 3 config servers, 2 or more ``mongos`` query routers, and at least 2 shards. The shards are replica sets.

  • shard: 分片,一个集合中的文档可能被拆分成若干个块,分别存储在不同服务器的MongoDB中
  • mongos: 类似于路由器,将增删改查请求路由至对应的分片上,如果你用过Mycat的话就更好理解了,把它当作Mycat即可.
  • config servers: 配置中心,用于存储Mongo的配置文件,当配置发生改变或者有节点宕机后恢复时提供配置信息,有点类似于spring-cloud中的统一配置中心config.

 

3.集群搭建

3.1环境

本篇演示使用3台服务器搭建的具有2个分片的集群,其中A服务器充当仲裁者.

使用的mongodb版本:

服务器操作系统:centos 7.3

三台服务器Ip:

serverA:192.168.174.129

serverB:192.168.174.132

serverC:192.168.174.133

架构图:

主机设计:

3.2步骤

MongoDB集群分片搭建步骤其实就是2.2中的那张图,搭建路由,搭建配置中心,搭建分片.

在正式搭建前,先做一些准备工作:

①关闭3台服务器上的防火墙(测试环境推荐,简单):

systemctl  stop firewalld 关闭防火墙

systemctl status firewalld 查看防火墙状态

生成环境的话建议把我在上面表格里提到的所有端口(20000,27017...)都给打开:

/sbin/iptables -I INPUT -p tcp --dport 20000 -j ACCEPT

iptables-save

...

②另外可以三台服务器互相ping一下,通过这些操作来避免到时候三台服务器无法互通.

③在每台服务器上分别安装MongoDB,安装我就不赘述了,不会的翻我前面的博客.

④在三台服务器上分别创建对应的数据文件夹:

#在serverA上
mkdir -p /data/{shard1-1,shard2-1,config}

#在serverB上
mkdir -p /data/{shard1-2,shard2-2,config}

#在serverC上
mkdir -p /data/{shard1-3,shard2-3,config}

#当然你也可以放在其他路径,在接下来操作中把对应的参数地址改掉即可

在完成以上操作以后,就可以正式进入配置步骤了.

步骤一:配置分片的复制集

①配置shard1的Replica Sets:

#在serverA上启动shard1-1
./mongod --fork --shardsvr --bind_ip 0.0.0.0 --port 27018 --dbpath /data/shard1-1/ --logpath /data/shard1-1/shard1-1.log --logappend --replSet shard1 --directoryperdb

#在serverB上启动shard1-2
./mongod --fork --shardsvr --bind_ip 0.0.0.0 --port 27018 --dbpath /data/shard1-2/ --logpath /data/shard1-2/shard1-2.log --logappend --replSet shard1 --directoryperdb

#在serverC上启动shard1-3
./mongod --fork --shardsvr --bind_ip 0.0.0.0 --port 27018 --dbpath /data/shard1-3/ --logpath /data/shard1-3/shard1-3.log --logappend --replSet shard1 --directoryperdb

#特别注意--bind_ip 0.0.0.0的添加,网上的教程配置不成功大多数是因为没有这句.

用mongo连接其中一台主机的27018端口的mongod,初始化复制集shard1: 
比如连接服务器C的27018端口,进入C服务器的mongdb安装目录的bin目录,并连接该端口:

./mongo 127.0.0.1:27018

然后执行下面操作以初始化复制集shard1:

config={ _id:'shard1',members:[{_id:0,host:'192.168.174.133:27018',priority:2},{_id:1,host:'192.168.174.132:27018',priority:1},{_id:2,host:'192.168.174.129:27018',arbiterOnly:true}] }
rs.initiate(config)

正确操作完成后会提示类似下面这种格式信息:

如果出现任何error,均为未完成配置,可以检查自己的参数,ip,端口等有无输错,如果均无法解决,可以参考文末我提出的几种常见的错误和解决办法自行解决.

②配置shard2的Replica Sets:


#在serverA上启动shard2-1
./mongod --fork --shardsvr --bind_ip 0.0.0.0 --port 27019 --dbpath /data/shard2-1/ --logpath /data/shard2-1/shard2-1.log --logappend --replSet shard2 --directoryperdb

#在serverB上启动shard2-2
./mongod --fork --shardsvr --bind_ip 0.0.0.0 --port 27019 --dbpath /data/shard2-2/ --logpath /data/shard2-2/shard2-2.log --logappend --replSet shard2 --directoryperdb

#在serverC上启动shard2-3
./mongod --fork --shardsvr --bind_ip 0.0.0.0 --port 27019 --dbpath /data/shard2-3/ --logpath /data/shard2-3/shard2-3.log --logappend --replSet shard2 --directoryperdb

用mongo连接其中一台主机的27019端口的mongod,初始化复制集shard1: 
比如连接服务器B的27019端口,进入B服务器的mongdb安装目录的bin目录,并连接该端口:

./mongo 127.0.0.1:27019

然后执行下面操作以初始化复制集shard2:

config={ _id:'shard2',members:[{_id:0,host:'192.168.174.133:27019',priority:2},{_id:1,host:'192.168.174.132:27019',priority:1},{_id:2,host:'192.168.174.129:27019',arbiterOnly:true}] }
rs.initiate(config)

 

成功后的提示与shard1类似,不贴图了.

步骤二:配置config server

在3台主机中分别启动配置服务:

./mongod --configsvr --bind_ip 0.0.0.0 --port 20000 --dbpath /data/config/ --logpath /data/config/config.log --replSet docdetection --logappend --fork

用mongo连接其中一台主机的20000端口的mongod,初始化复制集docdetection/

比如我连接A服务器的20000端口,并初始化复制集docdetection,进入A服务器MongoDB安装目录的bin目录并执行:

./mongo 127.0.0.1:20000

连接后执行:

configdb1={ _id:'docdetection',members:[{_id:0,host:'192.168.174.129:20000',priority:3},{_id:1,host:'192.168.174.132:20000',priority:1},{_id:2,host:'192.168.174.133:20000',priority:2}] }
rs.initiate(configdb1)

执行后如果没有报错,说明config server也配置成功了.

步骤三:配置router server

#在3台主机中分别运行mongos服务
./mongos --configdb docdetection/192.168.174.129:20000,192.168.174.132:20000,192.168.174.133:20000 --bind_ip 0.0.0.0 --port 27017 --logpath /data/mongos.log --logappend --fork

步骤四:配置分片(shard cluster)

连接到其中一台机器的端口27017的mongos进程,并切换到admin数据库添加分片shard1和shard2.

这里我以A服务器为例,进入其安装目录的bin目录下,执行:   ./mongon 127.0.0.1:20000连接到mongodb

然后并切换到admin数据库添加分片shard1和shard2:

use admin

db.runCommand({addshard:"shard1/192.168.174.129:27018,192.168.174.132:27018,192.168.174.133:27018"})
db.runCommand({addshard:"shard2/192.168.174.129:27019,192.168.174.132:27019,192.168.174.133:27019"})

#激活数据库(work)和集合(status)的分片功能:
db.runCommand({enablesharding:"work"})
db.runCommand({shardcollection:"work.status",key:{_id:1}})

如果以上操作都没有报错,至此一个具有2个复制集和2个分片一个仲裁者的高可用MongDB集群已经配置好了,我们可以使用命令查看一下状态:

pringShardingStatus()

 可以看出shards里已经有我们设计的两个分片shard1和shard2了,下面我们进入测试阶段,测试下到底有没有按预想的那样,数据可以分散到这两个分片当中.

3.3测试

#设置分片chunk大小
use config
db.settings.save({ "_id" : "chunksize", "value" : 1 })
设置1M是为了测试,否则要插入大量数据才能分片

#指定test分片生效
sh.enableSharding("test")

#创建索引
use test
db.users.createIndex({user_id : 1})

#指定数据库里需要分片的集合和片键
use admin
sh.shardCollection("test.users", {user_id: 1})


我们设置testdb的 users容器要分片,根据 user_id 自动分片到 shard1 ,shard2 上面去。要这样设置是因为不是所有mongodb 的库和容器都需要分片!
然后我们连接到其中一台服务器的20000端口,测试分片配置结果:
进入A服务器Mongo的安装目录的bin目录并执行:  ./mongo  127.0.0.1:20000 连接

use test;
#插入数据
for (var i = 1; i <=1000000; i++){
db.users.save({user_id: i, username: "user"+i});
}

插入过程会有点小卡顿,耐心等待下,如果没有响应的话可以过一会切换到其它服务器的20000端口连接进去查看.

#查看分片情况如下,部分无关信息省掉了
sh.status()

可以看到数据分到2个分片,各自分片chunk 数为: shard1 : 4,shard2 : 3 已经成功了!另外可以看到id从16914-36131的user被分布在了 shard1上,id从36131-54107的user被分布在了shard2上了,数据分布的还是比较均匀的,各项指标均符合预期.

4总结

一套配下来,感觉难度适中,只要不掉到坑里,还算可以接受,美中不足的是,希望Mongo官方在今后的版本里,能够像redis那样给出一些集群的配置脚本或者工具,不然在涉及服务器很多的情况下,很容易因为某些小细节配错导致流程走不通,即便都没有配错一套下来也会比较累.自己写shell脚本的话感觉也省不了太多操作,另外网上的教程良莠不齐,详略不一,谨慎参考,最靠谱的还是官网,如果看不懂可以去Mongo的中文社区看文档,教程结合官方文档基本上可以搞定过程中遇到的所有坑.

另外在正式环境中,可以将 ./mongo --configsvr --bind_ip 0.0.0.0 --port 20000 --dbpath /data/config/ --logpath......这种方式启动的mongo写进配置文件,比如A服务器的replica set的 可以写进 Areplica.yml(3.x以后的版本官方推荐使用yml格式,conf依旧支持),然后在启动./mongo时用 -f +路径/Areplica.yml去指定配置文件即可,这样做的好处就是如果服务器宕了,下次启动时无需再敲一遍了.

 

5.常见错误和坑

5.1防火墙 端口 bindIp 相关

> rs.initiate(config)
{
        "ok" : 0,
        "errmsg" : "replSetInitiate quorum check failed because not all proposed set members responded affirmatively: 192.168.xxx.xxx:27018 failed with No route to host, 192.168.xxx.xxx:27018 failed with No route to host",
       "code" : 74,
      "codeName" : "NodeNotFound"
}

如果你在初始化过程中出现类似上面这种错误,可以尝试关闭防火墙/生成环境下可以打开相应端口,然后将启动mongo时的参数中添加bindIp为0.0.0.0,使任何服务器都能连接到该服务,当然更为安全的做法是bingIp指定为该服务器所要连接到的服务器,比如A服务器和B服务器C服务器有联系,就添加它们2个的IP即可.

5.2初始化副本集报错

> rs.initiate(config)
{
         "ok" : 0,
        "errmsg" : "This node, 192.168.xxx.xxx:27019, with _id 0 is not electable under the new configuration version 1 for replica set shard2",
       "code" : 93,
       "codeName" : "InvalidReplicaSetConfig"
}

原因是:如果设置主机A为仲裁节点,那么不要在A主机上执行初始化操作,否则会报上述错误。

5.3启动路由器mongos报错

./mongos --configdb 192.168.xxx.xxx:20000,192.168.xxx.xxx:20000,192.168.xxx.xxx:20000 --port 27017 --logpath /data/mongos.log --logappend --forkFailedToParse: mirrored config server connections are not supported; for config server replica sets be sure to use the replica set connection stringtry './mongos --help' for more information

原因是:从Mongodb3.2之后,启动mongos时需使用副本集名称,否则会报上述错误。例如:

./mongos –configdb 副本集名称/192.168.xxx.xxx:20000,192.168.xxx.xxx:20000,192.168.xxx.xxx:20000 --port 27017 --logpath /data/mongos.log --logappend --fork

eg:./mongos --configdb docdetection/192.168.174.129:20000,192.168.174.132:20000,192.168.174.133:20000 --bind_ip 0.0.0.0 --port 27017 --logpath /data/mongos.log --logappend --fork

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值