MongoDB副本集分片使用场景两例

MongoDB如火如荼,特别引入了wiredTiger存储引擎之后(MMAPV0是全局锁,读写都要等,2.0之前的版本都是该引擎;3.0引入的MMAPV1是collection层级的锁定,做到了100%读并发。而wiredTiger是Document Level Lock,在不同的documents之间读写是可以完全并发执行的),更加如日中天。尽管它在内存管理方面仍有缺陷,但作为最接近传统关系型数据库的NoSQL来说,在各大企业生产环境中使用多多。官方在设计和开发时对于实用性确实下了功夫。紧追着时代步伐,宏观上紧密结合实际生产环境,细节上也处理得不错。

这篇笔记主要是记录一下生产环境下的两个实际使用场景:将副本集(冗余安全高可用)和分片(突破硬件瓶和承载高并发)。入门的知识这里就不做过多赘述啦,官方的文档足够啦。这也是官方团队努力的提现之一吧。

既然谈到实际生产环境,我也简单记录下对于部署前的一些考虑因素吧。

  • 集群的访问量,例如Q/S每秒读数量以及W/S峰值等。
  • 数据增长:3个月、12个月、36个月,企业规划的业务增长将对我们数据的冲击。
  • 考虑成本:硬件肯定是少不了的,另外就是维护的人员成本。

当然,这些只是架构上的前期考量。对于DBA而言,我们对数据结构本身的分析和判断自然是跑不掉的。

三台服务器:三个前端Mongos控制器 + 三个配置服务器副本集 + 三个后端数据存储副本集

我这里列出两种常见的场景下的部署:分别是用3和6台服务器来承载3个分片和副本集。我们先看第一种基于3台服务器的场景图示。

MongoDB Sharps and Replication Sets 3 Servers
The MongoDB Sharps & Replication Sets based upon 3 servers. Click for bigger image!

上图中的数字是mongod各个副本集节点、mongod config node和mongos的端口。当然,我们还可以使用更多的分片。不过,基于对于服务器性能比的考虑,个人觉得再加分片最好从硬件上进行扩展(例如:添加服务器)。另外,图中我们轻易发现,此架构的一个缺陷是Arbiter节点聚集的服务器的资源有点浪费;相对而言,secondary聚集的服务器又显得太具“侵略性”。我们是否可以单独再拉一台服务器(比如已经在线的,但不是很吃资源的服务器)来专门用以Arbiter节点?这样的话,我建议我们再增加一台服务器到各个副本集中,因为根据MongoDB选举机制的大多数原则,奇数节点的副本集利用率要大一些哦。当然,稍后我们6台服务器的架构也可以彻底解决上述缺陷。

MongoDB二进制包安装

我这里使用的是官方提供的二进制包,安装如下:

#192.168.122.109
cd /usr/src
curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.2.10.tgz
tar -zxvf mongodb-linux-x86_64-3.2.10.tgz
mkdir /usr/local/mongodb
cp -R -n mongodb-linux-x86_64-3.2.10/* /usr/local/mongodb
vim /etc/profile.d/path.sh #设置PATH,内容:export PATH=/usr/local/mongodb/bin:$PATH
source !$
echo $PATH

生产环境还是建议使用Package或自行编译安装,因为这个二进制包应该是不支持TLS/SSL加密的。

后端副本集搭建

配置第一台服务器上的副本集,如下:

#192.168.122.109
mkdir -p /var/mongodb/{data,log,conf}
mkdir /var/mongodb/data/{db29001,db29102,db29203,db28200} #我这里是为了方便之后操作,一起将本案例中本台服务器需要的数据库文件夹都一次性建立了。
useradd -s /sbin/nologin -M mongod
chmod -R 750 /var/mongodb/data
chown -R mongod:mongod !$
chown -R mongod:mongod /var/mongodb/log
openssl rand -out /var/mongodb/conf/.keyfile -base64 100 #产生副本集认证秘钥
vim /var/mongodb/conf/.keyfile #最后2个等号随便修改成[a-zA-Z+/]中任意字符, 我这里三个副本集都用的同一个keyfile,如果分开设置,请记得每组的节点配置中指向同一keyfile
chmod 600 !$
chown mongod:mongod !$
vim /var/mongodb/conf/29001.conf
##192.168.122.109:29001 repl1 primary##
net:
  port: 29001
 bindIp: 192.168.122.109,127.0.0.1
processManagement:
 fork: true
 pidFilePath: /tmp/mongod29001.pid
systemLog:
 destination: file
 path: /var/mongodb/log/29001.log
 logAppend: true
storage:
 dbPath: /var/mongodb/data/db29001
#replication: #这部分先注释掉。首次启动后建立个管理员账户再开启
 #oplogSizeMB: 1024
 #replSetName: repl1
security:
 authorization: enabled #keyFile设定了就不需要显式申明的
 #keyFile: /var/mongodb/conf/.keyfile 先不启用,启用了,下面的enableLocalhostAuthBypass就无效啦。
setParameter:
 enableLocalhostAuthBypass: true #只在无任何用户时生效
sharding: #这部分选举节点不用加
 clusterRole: shardsvr
####
curl https://raw.githubusercontent.com/mongodb/mongo/master/rpm/init.d-mongod > /etc/init.d/mongod29001
vim !$
##改变配置文件地址和二进制执行地址###
...
CONFIGFILE="/var/mongodb/conf/29001.conf"
LOCKFILE='/var/lock/subsys/mongod29001' #下面调用的地方也改一下
...
mongod=${MONGOD-mongod}
...
PIDFILEPATH=`awk -F'[:=]' -v IGNORECASE=1 '/^[[:blank:]]*(processManagement\.)?pidfilepath[[:blank:]]*[:=][[:blank:]]*/{print $2}' "$CONFIGFILE" | tr -d "[:blank:]\"'" | awk -F'#' '{print $1}'` #把开始和结尾的双引号去掉
###
chmod 755 !$
chkconfig --add mongod29001
chkconfig mongod29001 on
chkconfig --list mongod29001
yum install -y numactl.x86_64 #如果已安装的略过
service mongod29001 start
ps aux|grep mongod

创建管理员账户

第一个创建的用户的角色必须是root(userAdminAnyDatabase)、数据库为admin,如下:

#192.168.122.109
mongo 127.0.0.1:29001/admin
>db.createUser({user:'admin',pwd:'putty.biz',roles:[{role:'root',db:'admin'}]})
>exit
#192.168.122.137
mongo 127.0.0.1:29101/admin
>db.createUser({user:'admin',pwd:'putty.biz',roles:[{role:'root',db:'admin'}]})
>exit
#192.168.122.225
mongo 127.0.0.1:29201/admin
>db.createUser({user:'admin',pwd:'putty.biz',roles:[{role:'root',db:'admin'}]})
>exit

按照我这里的思路,记得回到预设的三个主节点配置文件中把replication、security两部分的注释移除再重启下mongod进程。如果觉得这样来回切麻烦,也可以参考官方指引(MongoDB强制使用keyfile的第六步中有为已有副本集创建管理员用户的方法)、紧接着,我们可以开始副本集成员设定和初始化啦。

副本集创建

这部分很简单,需要注意的就是我们这里是开启了安全设置的,所以每次在mongo shell中执行命令前记得验证下用户。

#192.168.122.109
mongo 192.168.122.109:29001/admin
>db.auth('admin','putty.biz') #返回1就是正常,这个和linux终端shell中相反
>config = {_id:'repl1',members:[{_id:0,host:'192.168.122.109:29001'},{_id:1,host:'192.168.122.137:29002'}]}
>rs.initiate(config)
repl1:OTHER> rs.addArb('192.168.122.225:29003') #初始化会有一会是OTHER,一会正常就称为primary啦
repl1:PRIMARY> rs.status() #检查状态
repl1:PRIMARY> rs.isMaster() #这个函数非常有用,副本集投票过程中也会反复调用哟
repl1:PRIMARY> config=rs.config()
repl1:PRIMARY> config.members[0].priority=2 #提升权重为2,默认都是1。
rs.reconfig(config) #重新应用配置

secondary验证同步


#192.168.122.137
mongo 192.168.122.137:29002/admin
repl1:SECONDARY>db.auth('admin','putty.biz')
repl1:SECONDARY> rs.slaveOk(true) #shell连上secondary节点默认是不可以查询的,设置后才可以使用查询,方便我们校验
repl1:SECONDARY> use local
repl1:SECONDARY> db.system.replset.find().pretty()
repl1:SECONDARY> exit

其他两个服务器上的副本集节点添加是一样的操作。我这里就不列举啦。我们也可以直接通过设置成员参数来调整更多设置(请参考官方文档,不是本博文范畴)。

分片配置服务器搭建

在一个完整的mongodb分片架构中,除了接收前端请求的mongos进程和后端的存储数据节点(我们上面搭建的三个副本集),中间是一台分片配置服务器。它主要负责存储元数据. 它的架设与普通的mongodb实例没有太大区别. 不过,MongoDB 3.2开始,我们可以将配置服务器也搭建成副本集。官方给出好处是:更好的保持配置节点的数据一致性以及添加让一个分片集群拥有超过3个以上的配置节点(一个副本集可以有50个节点)。另外,配置节点副本集成员使用的数据存储引擎必须为wiredTiger(3.2默认的就是).

3.2之前的版本要求每个分片集群拥有3个镜像配置节点,而且三者的时区时间设置要同步(利用NTP服务)。下面我是直接新建,所以按照副本集配置即可,只是不能有选举节点出现在配置副本集。如果是老分片集群升级,可以参考官方文档:分片镜像配置节点升级到副本集和无缝对接方法。

下面是配置节点副本集搭建过程:

mkdir /var/mongodb/data/db28200
chowm -R mongod.mongod !$
chomod -R 750 !$
vim /var/mongodb/conf/28200.conf
#####sharding config server sample####
net:
 port: 28200
 bindIp: 192.168.122.109,127.0.0.1
processManagement:
 fork: true
 pidFilePath: /tmp/mongod28200.pid
systemLog:
 destination: file
 path: /var/mongodb/log/28200.log
 logAppend: true
storage:
 dbPath: /var/mongodb/data/db28200
security:
 authorization: enabled
 keyFile:/var/mongodb/conf/.keyfile
setParameter:
 enableLocalhostAuthBypass: true
replication:
 replSetName: replconf
sharding:
 clusterRole: configsvr
#####
cp /etc/init.d/mongod29001 /etc/init.d/mongod28200
vim !$
###
...
CONFIGFILE="/var/mongodb/conf/28200.conf"
...
###
chkconfig --add mongod28200
service mongod28200 start
ps aux |grep mongod
netstat -lnp|grep 28200
mongo 127.0.0.1:28202/admin
configsvr> db.createUser({user:'admin',pwd:'putty.biz',roles:[{role:'root',db:'admin'}]}) #执行这一步先注销掉keyfile
vim /var/mongodb/conf/28200.conf #移除备注,使其成为使用keyfile验证的单节点配置副本集
service mongod28200 restart

关于管理员和分片簇群用户的建立,可以参考官方文档:keyfile认证在分片集群中的应用
另外两个节点一样配置好。下面我们就可以添加成员节点和初始化啦。

初始化配置副本集

登录到任意一个配置节点,我们开始初始化该副本集。

mongo 192.168.122.109:28200/admin
>db.auth"('admin','putty.biz')
>config={_id:'replconf',configsvr:true,members:[{_id:0,host:'192.168.122.109:28200'},{_id:1,host:'192.168.122.137:28201'},{_id:2,host:'192.168.122.225:28202'}]}
>rs.initiate(config)

这样,我们的配置副本集就搞定啦。最后,我们来启动mongos进程,它主要负责路由消息。它有独立的二进制进程,并不是mongod。而且,它是不需要数据库的。

控制器Mongos:路由服务节点搭建


#192.168.122.109
cd /var/mongodb/conf
cp 29001.conf 28100.conf
vim !$
###
net:
 port: 28100
 bindIp: 192.168.122.109,127.0.0.1
processManagement:
 fork: true
 pidFilePath: /tmp/mongod28100.pid
systemLog:
 destination: file
 path: /var/mongodb/log/28100.log
 logAppend: true
security:
 keyFile: /var/mongodb/conf/.keyfile
sharding:
 configDB: replconf/192.168.122.109:28200,192.168.122.137:28201,192.168.122.225:28202
###

Mongs启动脚本


cp /etc/init.d/mongod28200 /etc/init.d/mongos28100
vim !$
###
CONFIGFILE="/var/mongodb/conf/28100.conf"
mongod=${MONGOD-mongos} #这里不再是mongod
###
chkconfig --add mongos28100
service mongos28100 start
ps aux|grep mongos
netstat -lnp|grep 28100
mongo 192.168.199.122:28100
db.auth('admin','putty.biz') #2.6版开始,分片管理员信息被存储在配置服务器上,Mongos服务本身就不带有数据库。所以这里直接使用config服务器上的管理员账户即可。

其他两台服务器上也照例搭建好服务器。我这里不列举代码啦。

我示例中选择的端口每个都节点都有差异,主要是为了起到演示区分的作用。生成环境下,配合自动化运维工具,我们一般会选择不同服务器同一端口来起各自响应服务。

分片成员添加

到此,我们的分片副本集已经全部搭建完成。之后,我们简单演示如何去使用它,一共有3步:

  1. 在控制器上利用sh.addShard(“”)指令,添加分片副本集节点。
  2. 在控制器上利用sh.enableSharding(““)指令,针对需要的数据库开启分片功能。
  3. 在控制器上利用sh.shardCollection(“.“,{:})指令,对记录集开启分片功能。如果是已有数据的记录集,必须使用ensureIndex(createIndex)先创建索引。

我这里假设会有一个库叫做puttybiz,下面会有一个记录集user, 我用email为片键。具体操作如下:

#192.168.122.109:28100
mongo 192.168.122.109:28100/admin
mongos> db.auth('admin','putty.biz')
mongos> sh.addShard('repl1/192.168.122.109:29001,192.168.122.137:29002,192.168.122.225:29003') #或者使用db.runCommand({addShard:'repl1/192.168.122.109:29001,192.168.122.137:29002,192.168.122.225:29003',name:'shard1',maxsize:20480}); 可以自定义shard名称,addShard指令用的是副本集名称。
mongos> sh.addShard('repl2/192.168.122.109:29102,192.168.122.137:29101,192.168.122.225:29103')
mongos> sh.addShard('repl3/192.168.122.109:29203,192.168.122.137:29202,192.168.122.225:29201')
mongos> sh.shardCollection("puttybiz.user",{email:1})

分片管理命令


mongos> use puttybiz
mongos> db.user.stats() #查看记录集状态,sharded为true表示当前记录集已经启用了分片
mongos> db.printShardingStatus() #查看分片整体情况
mongos> use config
mongos> db.shards.find() #查看成员

测试数据


mongo 192.168.122.109:28100/admin
mongos> db.auth('admin','putty.biz')
mongos> use putybiz
mongos> for(i=0;i<10000;i++){db.user.insert({userid:i,email:"p"+i+"@putty.biz"})} mongos> db.printShardingStatus() #可以看到我们刚才插入的测试数据被按照片键分不到不同的后端副本集分片成员中去了

六台服务器: 三个前端控制器节点 + 三个配置节点 + 三个后端数据存储副本集

部署过程跟之前的是大同小异,只不过前期规划(或者扩容)时采用了不同数量的服务器,主要还是为了应对更高的并发访问。与此同时,服务器更多意味着我们可以去提升利用率(例如尽量将后端数据真实存储的副本集中的投票节点平均分布到不同的服务器)。

基于6台服务器的副本集分片架构示意图
为了应对更高并发,与基于3台服务器的副本集分片部署,6台的性能提升一倍。

篇幅原因,我这里只提供一个架构示意图(如上图,单击可放大)。大家有兴趣,也可以在虚拟机环境下搭建测试一番。