centos配置mongodb副本集

2017-08-30 15:34:04 查看 1529 回复 0

那什么是副本集呢?打魔兽世界总说打副本,其实这两个概念差不多一个意思。游戏里的副本是指玩家集中在高峰时间去一个场景打怪,会出现玩家暴多怪物少的情况,游戏开发商为了保证玩家的体验度,就为每一批玩家单独开放一个同样的空间同样的数量的怪物,这一个复制的场景就是一个副本,不管有多少个玩家各自在各自的副本里玩不会互相影响。 mongoDB的副本也是这个,主从模式其实就是一个单副本的应用,没有很好的扩展性和容错性。而副本集具有多个副本保证了容错性,就算一个副本挂掉了还有很多副本存在,并且解决了上面第一个问题“主节点挂掉了,整个集群内会自动切换”。难怪mongoDB官方推荐使用这种模式。我们来看看mongoDB副本集的架构图:

mongorep2

由图可以看到客户端连接到整个副本集,不关心具体哪一台机器是否挂掉。主服务器负责整个副本集的读写,副本集定期同步数据备份,一但主节点挂掉,副本节点就会选举一个新的主服务器,这一切对于应用服务器不需要关心。我们看一下主服务器挂掉后的架构:

mongodb故障转移

副本集中的副本节点在主节点挂掉后通过心跳机制检测到后,就会在集群内发起主节点的选举机制,自动选举一位新的主服务器。看起来很牛X的样子,我们赶紧操作部署一下! 官方推荐的副本集机器数量为至少3个,那我们也按照这个数量配置测试。

上面是对副本集的解释,下面开始操作!

首先先去下载一个mongodb,版本选择随意,选择一个稳定版本就行个人推荐3.X.X的版本。

下载地址https://www.mongodb.org/dl/linux/x86_64-amazon

   下载 wget http://downloads.mongodb.org/linux/mongodb-linux-x86_64-amazon3.4.7.tgz
    解压 tar -zxvf mongodb-linux-x86_64-amazon3.4.7.tgz  

文件相关操作自行脑补。。。
最终文件夹(个人习惯)

/home/mongo/bin   //mongo文件夹
/home/mongo/data   //mongo数据文件夹
/home/mongo/log   //mongo日志文件夹

献上一份启动配置文件

port=27017
replSet=mySet
dbpath=/home/mongo/data/
logpath=/home/mongo/log/mongodb.log
logappend=true
directoryperdb=true
journal=true
fork=true
smallfiles=true
maxConns=20000

参数解释:

dbpath:数据存放目录

logpath:日志存放路径

logappend:以追加的方式记录日志

replSet:replica set的名字

port:mongodb进程所使用的端口号,默认为27017

fork:以后台方式运行进程

journal:写日志

smallfiles:当提示空间不够时添加此参数

其他参数

pidfilepath:进程文件,方便停止mongodb

directoryperdb:为每一个数据库按照数据库名建立文件夹存放

bind_ip:mongodb所绑定的ip地址

oplogSize:mongodb操作日志文件的最大大小。单位为Mb,默认为硬盘剩余空间的5%

noprealloc:不预先分配存储

开始配置:

三台机器 192.168.0.2(主),192.168.0.3(从),192.168.0.4(仲裁)

分别在每台机器上启动mongodb

cd /home/mongo/bin
./mongod -f /home/mongo/mongo.conf

*别说找不到配置文件,在上方都看不到,表示你不适合搞这个。

有这样的提示说明启动成功

tttt

全部三个服务全部启动成功之后开始配置主(master),备(slaver),仲裁(arbiter)节点

初始化副本集

在三台机器上任意一台机器登陆mongodb

/home/mongo/bin/mongo
 
#使用admin数据库
use admin

#定义副本集配置变量,这里的 _id:”repset” 和启动参数“ –replSet” 要保持一样。

cfg={ _id:"mySet", members:[ {_id:0,host:'192.168.0.2:27017',priority:2}, {_id:1,host:'192.168.0.3:27017',priority:1},{_id:2,host:'192.168.0.4:27017',arbiterOnly:true}] }; 

个人比较喜欢的一种方式,权限类型一下搞定。

#初始化副本集配置
rs.initiate(cfg);

#输出成功

{
        "info" : "Config now saved locally.  Should come online in about a minute.",
        "ok" : 1
}

#查看状态

#查看集群节点的状态
 rs.status();

#输出

{
        "set" : "mySet",
        "date" : ISODate("2017-08-30T08:18:59.820Z"),
        "myState" : 2,
        "term" : NumberLong(7),
        "syncingTo" : "172.18.73.13:27017",
        "heartbeatIntervalMillis" : NumberLong(2000),
        "members" : [
                {
                        "_id" : 0,
                        "name" : "192.168.0.2:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 23176,
                        "optime" : {
                                "ts" : Timestamp(1504081121, 621),
                                "t" : NumberLong(7)
                        },
                        "optimeDate" : ISODate("2017-08-30T08:18:41Z"),
                        "lastHeartbeat" : ISODate("2017-08-30T08:18:58.394Z"),
                        "lastHeartbeatRecv" : ISODate("2017-08-30T08:18:58.394Z"),
                        "pingMs" : NumberLong(0),
                        "electionTime" : Timestamp(1504056061, 1),
                        "electionDate" : ISODate("2017-08-30T01:21:01Z"),
                        "configVersion" : 1
                },
                {
                        "_id" : 1,
                        "name" : "192.168.0.2:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 23176,
                        "optime" : {
                                "ts" : Timestamp(1504081139, 620),
                                "t" : NumberLong(7)
                        },
                        "optimeDate" : ISODate("2017-08-30T08:18:59Z"),
                        "syncingTo" : "172.18.73.13:27017",
                        "configVersion" : 1,
                        "self" : true
                },
                {
                        "_id" : 2,
                        "name" : "192.168.0.3:27017",
                        "health" : 1,
                        "state" : 7,
                        "stateStr" : "ARBITER",
                        "uptime" : 23176,
                        "lastHeartbeat" : ISODate("2017-08-30T08:18:58.394Z"),
                        "lastHeartbeatRecv" : ISODate("2017-08-30T08:18:56.260Z"),
                        "pingMs" : NumberLong(0),
                        "configVersion" : 1
                }
        ],
        "ok" : 1
} 

其他操作

增添节点
添加节点,需要在主节点进行

PRIMARY>>rs.add(hostname:port)

删减节点
删减节点,需要在主节点进行

PRIMARY>rs.remove(hostname:port)

设置副本节点可以读

设置节点可读,需要在副本节点进行

SECONDARY> db.getMongo().setSlaveOk();

设置副本节点优先级

设置优先级,需要在主节点进行。通过修改priority的值来实现(默认的优先级是1(0-100),priority的值设的越大,就优先成为主。如果值是0,那么不能成为primay

PRIMARY> config=rs.conf()
PRIMARY>config.members[3].priority = 3
PRIMARY> rs.reconfig(config)

注意:members大括号里面的成员和_id是没有关系的,而是rs.conf查出来节点的数值的顺序;

 相关资料

节点数量
副本集至少需要两个节点,至多12个节点。其中一个是主节点,其余的都是从节点和仲裁节点。具备投票权的节点至多为7个。从节点越多,复制的压力就越大,备份太多反而增加了网络负载和拖慢了集群性能。
MongoDB官方推荐节点数量为奇数。主要在于副本集常常为分布式,可能位于不同的IDC。如果为偶数,可能出现每个IDC的节点数一样,从而如果网络故障,那么每个IDC里的节点都无法选出主节点,导致全部不可用的情况。比如,节点数为4,分处于2个IDC,现在IDC之间的网络出现故障,每个IDC里的节点都没有大于2,所以副本集没有主节点,变成只读。

从节点的类型
Secondary:标准从节点,可复制,可投票,可被选为主节点
Secondary-Only:不能成为主节点,只能作为从节点,防止一些性能不高的节点成为主节点。设置方法是将其priority = 0
Hidden:这类节点是不能够被客户端制定IP引用,也不能被设置为主节点,但是可以投票,一般用于备份数据。
Delayed:可以指定一个时间延迟从primary节点同步数据。主要用于备份数据,如果实时同步,误删除数据马上同步到从节点,恢复又恢复不了。
示例:

cfg= {
   "_id" : ,
   "host" : ,
   "priority" : 0,
   "slaveDelay" : ,
   "hidden" : true
}
rs.initiate(cfg);

Non-Voting:没有选举权的secondary节点,纯粹的备份数据节点。设置方法是将其votes = 0。之所以设置这种节点,是照顾副本集最多12个节点,但有投票权的节点最多7个节点的要求。
示例:

PRIMARY>cfg = rs.conf()
PRIMARY>cfg.members[3].votes = 0
PRIMARY>cfg.members[4].votes = 0
PRIMARY>cfg.members[5].votes = 0
PRIMARY>rs.reconfig(cfg)

读写分离
从节点默认情况下不能读取,但可以设置此选项:

SECONDARY> db.getMongo().setSlaveOk();

这样,读取部分可以使用这些开启了读取选项的从节点,从而减轻主节点的负荷。
至于写入,永远只有主节点才可以进行。

最终一致性

数据的写入在主节点进行,将操作记录进日志oplog,从节点定期轮询主节点,获取oplog,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致,因此有一定时间的滞后是必然的,MongoDB追求的是最终一致性。既然“滞后”不可避免,需要做的就是尽可能减小这种滞后,主要涉及到以下几点:

网络延迟:这是所有分布式系统都存在的问题。我们能做的就是尽可能减小副本集节点之间的网络延迟。

磁盘吞吐量:secondary节点上数据刷入磁盘的速度比primary节点上慢的话会导致secondary节点很难跟上primary节点的节奏。

并发:并发大的情况下,primary节点上的一些耗时操作会阻塞secondary节点的复制操作,导致复制操作跟不上主节点的写入负荷。解决方法是通过设置操作的write concern(参看这里:http://docs.mongodb.org/manual/core/write-concern/#replica-set-write-concern)默认的副本集中写入操作只关心primary节点,但是可以指定写入操作同时传播到其他secondary节点,代价就是严重影响集群的并发性。注意:而且这里还存在一个问题如果,如果写入操作关心的某个节点宕机了,那么操作将会一直被阻塞直到节点恢复。

适当的write concern:我们为了提高集群写操作的吞吐量经常会将writer concern设置为unacknowledged write concern,这导致primary节点的写操作很快而secondary节点复制操作跟不上。解决方法和第三点是类似的就是在性能和一致性之间做权衡。

副本集不可用的应对之道
当节点发生故障,或者因为网络原因失联,造成余下的节点小于等于副本集总节点数的一半,那么整个副本集中都不再存在主节点,原来的主节点会降级成为从节点!此时整个副本集呈现只读状态,也即不再可用。
当出现这种瘫痪情况,目前我还没找到将某个从节点切换为主节点的方法,也许本来就不存在这种方法。因为这和MongoDB的Primary选举策略有关:如果情况不是Secondary宕机,而是网络断开,那么可能出现位于不同IDC的节点各自选取自己为Primary。这样在网络恢复后就需要处理复杂的一致性问题。而且断开的时间越长,时间越复杂。所以MongoDB选择的策略是如果集群中存活节点数量不够,则不选取Primary。
所以
1) 一方面要极力避免出现这种存活节点不够半数的情况,在规划副本集的时候就注意:
设置仲裁节点。
节点总数为奇数,且主节点所在的IDC,拥有超过半数的节点数量
2) 注意对节点的备份,必要时可以对节点进行恢复
也可以按照相同配置建立一个全新的从节点,恢复副本集后,系统会自动同步数据。但猜测数据量比较大的情况下,耗时会比较长,所以平时对从节点进行备份,还是有必要。


使用一段时间后的出现的问题:mongo内存占用量
监控了下mongo使用 内存使用高达87%,搞的其他线程都跑不了。

这里需要限制下mongo的使用。

后来网上找了下,发现cgroup这个东西可以限制内存使用,于是就去找了相关资料,并自己配置试验了下,发现可以,现在线上系统用这个跑了快一周了,暂时没发现什么问题,内存被限制住了。

cgroup是linux内核的一个功能,用于管理系统各种资源,包括cpu、内存等其他资源,个人只看了下内存的使用。可以直接使用cgroup的相关命令配置,也可以使用预先配置cgconfig服务的相关配置文件来管理。

一、安装:

a、cgroup是linux系统的一个内核组件,只需要安装控制接口程序 

 yum install -y libcgroup 

b、启动cgroup的管理程序

service cgconfig start 

查看cgconfig是否启动

service cgconfig status 

设置开机启动  

chkconfig cgconfig on 

二、配置:

a、先看cgconfig配置文件:

[root@mongo1 mongodb]# cat /etc/cgconfig.conf 
        mount {
            cpuset  = /cgroup/cpuset;
            cpu = /cgroup/cpu;
            cpuacct = /cgroup/cpuacct;
            memory  = /cgroup/memory;
            devices = /cgroup/devices;
            freezer = /cgroup/freezer;
            net_cls = /cgroup/net_cls;
            blkio   = /cgroup/blkio;
        }
 
        group DBLimitedGroup {
                memory {
                        memory.limit_in_bytes = "32212254720" ;
 
                }
 
 
        }

上面配置文件中,mount{...} 这一部分是安装libcgroup后生成的/etc/cgconfig.conf中默认有的,目的是定义各个子系统的挂载点,可以不用管。

为了限制mongodb,这里定义了一个group名字叫做DBLimitedGroup,这个group中定义了对内存memory进行限制,限制的项为memory.limit_in_bytes ,它的值为32212254720(32212254720/1024/1024/1024=30GB),线上服务器是32G的内存,这里设置限制为30G给mongodb。根据自己的情况设置,数值的单位为 byte字节,比如1G=1*1024*1024*1024=1073741824(byte)

b、然后看cgrules.conf

cat  /etc/cgrules.conf 
 
# /etc/cgrules.conf
#The format of this file is described in cgrules.conf(5)
#manual page.
#
# Example:
#<user>       <controllers> <destination>
#@student   cpu,memory  usergroup/student/
#peter      cpu     test1/
#%      memory      test2/
# End of file
 
*:mongod    memory      DBLimitedGroup/

这个cgrules.conf是配置cgexec命令的默认配置,后面会用到cgexec来执行mongod的程序,"#"开头是注释,不用管,看最后一行。

上面的意思为:

*     所有的用户以及用户组

mongod   mongod程序名(进程)

memory  使用memory子系统中的配置

DBLimitedGroup   cgroup.conf中自定义的group

全部连在一起就是说,执行cgexec时,如果满足“任何用户任何组执行mongod程序,那么被执行的进程(及mongod进程)将满足memory内存子系统中的DBLimitedGroup中的所有限制”

而DBLimitedGroup中的限制就是,限制内存使用为32G。

这样使用cgroup就限制了指定进程(这里是mongod进程)的(物理)内存使用。

配置完成后重新启动cgconfig

service cgconfig restart

三、运行mongodb

cgexec   -g memory:DBLimitedGroup /usr/bin/mongod -f /etc/mongodb/shard11.conf

本文参考:

http://blog.csdn.net/leftfist/article/details/39583585

http://www.lanceyan.com/tech/mongodb/mongodb_repset1.html

http://xiaotong.blog.51cto.com/4312502/1783768