MongoDB 集群分片(涉及分片、副本集等)

     # MongoDB 的命令对大小写是比较敏感的,所以,注意命令大小写问题:
一、MongoDB 集群分片的组成:
mongodb 的集群分片包括 4 个部分: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:
          分片如果没有 replica set 是个不完整架构;
          假设,其中的一个分片挂掉那四分之一的数据就丢失了,所以,在高可用性的分片架构,还需要对于每一个分片构建 replica set 副本集保证分片的可靠性;
          生产环境通常是 2个副本 + 1个仲裁。
二、MongoDB 集群分片部署规划和集群架构图:
     1.机器或实例数量:
     下面,我们确定一下,机器的大体数量:
          mongos 3个, config server 3个,数据分3片 shard server 3个,每个shard 有一个副本一个仲裁也就是 3 * 2 = 6 个,总共需要部署15个实例
          但是,我们这里只是模拟测试,就准备 4 台机器:
172.16.10.53;172.16.10.54;172.16.10.55;172.16.10.56;
     2.实例端口配置部分:
          这里,由于有的实例部署在同一台机器上,所以进行了端口规划:
          mongos:          20000
          config server:   21000
          shard1:             22001
          shard2:             22002
          shard3:             22003
     3.MongoDB 集群分片架构图:
                  3
三、MongoDB 集群分片部署
     1.MongoDB 部署:
          默认,已经初始安装好了,安装在 /usr/local/mongodb
     2.MongoDB 多实例 部署:
        # 以下操作,在 172.16.10.54,172.16.10.55 上执行
        (1).新建 MongoDB分片 的 data 目录:(切换到 mongodb 用户下,新建目录)
               mkdir -p   /usr/local/mongodb/data/share_0{1..3}
        (2).在conf 目录,新建并编辑 3份 配置文件(可用root用户新建编辑)
               vim  /usr/local/mongodb/conf/mongodb_01.conf
                    port=22001
                    dbpath=/usr/local/mongodb/data/share_01
                    logpath=/usr/local/mongodb/log/mongodb_01.log
                    logappend=true
                    fork=true
                    oplogSize=4096
                   # replSet=share_01/172.16.10.56:22001,172.16.10.55:22001   # 在10.54 上配置文件,去掉此行前面的注释
                   # replSet=share_01/172.16.10.56:22001,172.16.10.54:22001   # 在10.55 上配置文件,去掉此行前面的注释
               vim  /usr/local/mongodb/conf/mongodb_02.conf
                    port=22002
                    dbpath=/usr/local/mongodb/data/share_02
                    logpath=/usr/local/mongodb/log/mongodb_02.log
                    logappend=true
                    fork=true
                    oplogSize=4096
                   # replSet=share_02/172.16.10.56:22002,172.16.10.55:22002   # 在10.54 上配置文件,去掉此行前面的注释
                   # replSet=share_02/172.16.10.56:22002,172.16.10.54:22002   # 在10.55 上配置文件,去掉此行前面的注释
               vim  /usr/local/mongodb/conf/mongodb_03.conf
                    port=22003
                    dbpath=/usr/local/mongodb/data/share_03
                    logpath=/usr/local/mongodb/log/mongodb_03.log
                    logappend=true
                    fork=true
                    oplogSize=4096
                   # replSet=share_03/172.16.10.56:22003,172.16.10.55:22003   # 在10.54 上配置文件,去掉此行前面的注释
                   # replSet=share_03/172.16.10.56:22003,172.16.10.54:22003   # 在10.55 上配置文件,去掉此行前面的注释
 
        (3).MongoDB 启动测试:
             # 切换用户:
               su – mongodb
             # 启动:
               /usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/mongodb_01.conf
               /usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/mongodb_02.conf
               /usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/mongodb_03.conf
             # 查看是否启动:
                 ps -ef | grep mongodb   或    netstat -tulnap | grep mongo
     3.MongoDB 仲裁配置:【 这个操作在 172.16.10.56 】
        (1).MongoDB 仲裁的安装:
             MongoDB arbiter 安装  和 mongodb 的安装一样(请参照mongodb安装),只需要修改一下配置文件;
             我们,这里修改 mongodb 仲裁的名字叫 mongodb_arbiter,安装在 /usr/local/mongodb_arbiter 目录下;
        (2).MongoDB 仲裁多实例部署 和 上面 mongodb 的多实例部署一致:
             新建 MongoDB的 data 目录:(切换到 mongodb 用户下,新建目录)
               mkdir -p   /usr/local/mongodb/data/arbiter_0{1..3}
        (3).在conf 目录,新建并编辑 3份 配置文件(可用root用户新建编辑)
               vim /usr/local/mongodb_arbiter/conf/mongodb_01.conf
                    port=22001
                    dbpath=/usr/local/mongodb_arbiter/data/arbiter_01
                    logpath=/usr/local/mongodb_arbiter/log/arbiter_01.log
                    logappend=true
                    fork=true
                    replSet=share_01/172.16.10.54:22001,172.16.10.55:22001
               vim /usr/local/mongodb_arbiter/conf/mongodb_02.conf
                    port=22002
                    dbpath=/usr/local/mongodb_arbiter/data/arbiter_02
                    logpath=/usr/local/mongodb_arbiter/log/arbiter_02.log
                    logappend=true
                    fork=true
                    replSet=share_02/172.16.10.54:22002,172.16.10.55:22002
               vim /usr/local/mongodb_arbiter/conf/mongodb_03.conf
                    port=22003
                    dbpath=/usr/local/mongodb_arbiter/data/arbiter_03
                    logpath=/usr/local/mongodb_arbiter/log/arbiter_03.log
                    logappend=true
                    fork=true
                    replSet=share_03/172.16.10.54:22003,172.16.10.55:22003
        (4).mongodb_arbiter启动:
               /usr/local/mongodb_arbiter/bin/mongod –config /usr/local/mongodb_arbiter/conf/mongodb_01.conf
               /usr/local/mongodb_arbiter/bin/mongod –config /usr/local/mongodb_arbiter/conf/mongodb_02.conf
               /usr/local/mongodb_arbiter/bin/mongod –config /usr/local/mongodb_arbiter/conf/mongodb_03.conf
     4.MongoDB 备份集配置:
          # 修改 172.16.10.54,172.16.10.55  三个 mongodb 实例的 配置:
          # 注:
                    修改的部分,就是实例 注释掉的部分,根据需要,去掉需要部分前面的“ # ” ,重启即可;
          # 以上配置修改完成后,需要重启 54,55 上的实例;
          # 在 仲裁端 登录,初始化副本集:
              登录到172.16.10.54:22001 (mongodb master)上,执行如下操作:
                     /usr/local/mongodb/bin/mongo –port 22001
                     /usr/local/mongodb/bin/mongo>use admin
                     /usr/local/mongodb/bin/mongo>config_share01={_id:’share_01‘,members:[
                                                                                                     {_id:0,host:’172.16.10.54:22001‘,priority:10},
                                                                                                     {_id:1,host:’172.16.10.55:22001‘,priority:9},
                                                                                                     {_id:2,host:’172.16.10.56:22001‘,”arbiterOnly”:true} ]}
                     /usr/local/mongodb/bin/mongo>rs.initiate(config_share01)
                     /usr/local/mongodb/bin/mongo>rs.status()
                     /usr/local/mongodb/bin/mongo>rs.config()
          # 其它实例,修改 红色部分端口,和分片号; 这里就不做副本集测试
     5.MongoDB config 服务 和 mongos(路由) 服务部署:
#   在172.16.10.53 上部署;
        #   配置服务和路由服务的安装,只需要正常安装mongodb, 修改相应的配置文件即可:
        #   新建配置服务数据目录(以mongodb身份新建)
             mkdir -p /usr/local/mongodb/data/config_server
        (1).修改 MongoDB  配置服务器的配置文件:
             vim /usr/local/mongodb/conf/config_server.conf
               pidfilepath = /usr/local/mongodb/config/mongodb_config.pid 
               logpath =/usr/local/mongodb/log/config_server.log  
               dbpath = /usr/local/mongodb/data/config_server
               directoryperdb = true  
               configsvr = true  
               port = 21000  
               logappend = true  
               fork = true 
 
        (2).修改 MongoDB  路由服务器的配置文件:
             vim /usr/local/mongodb/conf/mongos.conf
               # 监听的配置服务器,只能有1个或者3个
               configdb = 172.168.10.53:21000   
               port = 20000  
               # 单位 mb 生成环境请使用 100 或删除,删除后默认是64
               chunkSize = 1 
               logpath =/usr/local/mongodb/log/mongos.log  
               logappend = true  
               fork = true  
        (3).启动配置服务器和路由服务器:
               A.配置服务器的启动:
                    /usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/config_server.conf
               B.路由服务器的启动:
                    /usr/local/mongodb/bin/mongos –config /usr/local/mongodb/conf/mongos.conf
      6.MongoDB 路由节点配置:
          /usr/local/mongodb/bin/mongo –port 20000
          mongos> use admin
          mongos> db.runCommand({addshard:”share_02/172.16.10.54:22002,172.16.10.55:22002,172.16.10.56:22002″,name:”share_02″,maxsize:20480})
          mongos> db.runCommand({addshard:”share_01/172.16.10.54:22001,172.16.10.55:22001,172.16.10.56:22001″,name:”share_01″,maxsize:20480})
mongos> db.runCommand({addshard:”share_03/172.16.10.54:22003,172.16.10.55:22003,172.16.10.56:22003″,name:”share_03″,maxsize:20480})
mongos> db.runCommand({listshards:1})
               {
          “shards” : [
     {
“_id” : “share_02”,
“host” : “share_02/172.16.10.54:22002,172.16.10.55:22002”
     },
     {
“_id” : “share_01”,
“host” : “share_01/172.16.10.54:22001,172.16.10.55:22001”
     },
     {
“_id” : “share_03”,
“host” : “share_03/172.16.10.54:22003,172.16.10.55:22003”
     }
          ],
          “ok” : 1
               }
               #  注:
                     replica set + shard 功能就配置好了,注意:虽然配置好了,但是还要声明库和表要进行分片
      7.MongoDB 声明库 和 表 分片:
          mongos> use admin  # 切换到admin库
          mongos> db.runCommand({enablesharding:”test2″});   # 声明test2库允许分片
          mongos> db.runCommand( { shardcollection : “test2.books”, key : { id : 1 } } );   # 声明books表要分片
          mongos> use test2    # 切换到test2
          mongos> db.stats();   # 查看数据库状态
          mongos> db.books.stats();   # 查看表状态
四、MongoDB 集群分片测试:
     1.测试脚本:
          mongos> for ( var i=1;i<=20000;i++) db.books.save({id:i,name:”haowu”,sex:”male”,age:28,value:”haowu”});
     2.测试结果:
          (1).这个插入分片数据前的信息:
mongos> db.books.stats();
{
“sharded” : true,
“paddingFactorNote” : “paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only.”,
“userFlags” : 1,
“capped” : false,
“ns” : “test2.books”,
“count” : 0,
“numExtents” : 1,
“size” : 0,
“storageSize” : 8192,
“totalIndexSize” : 16352,
“indexSizes” : {
“_id_” : 8176,
“id_1” : 8176
},
“avgObjSize” : 0,
“nindexes” : 2,
“nchunks” : 1,
“shards” : {
“share_01” : {
“ns” : “test2.books”,
“count” : 0,
“size” : 0,
“numExtents” : 1,
“storageSize” : 8192,
“lastExtentSize” : 8192,
“paddingFactor” : 1,
“paddingFactorNote” : “paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only.”,
“userFlags” : 1,
“capped” : false,
“nindexes” : 2,
“totalIndexSize” : 16352,
“indexSizes” : {
“_id_” : 8176,
“id_1” : 8176
},
“ok” : 1,
“$gleStats” : {
“lastOpTime” : Timestamp(0, 0),
“electionId” : ObjectId(“5551b02945399f93109d3a39”)
}
}
},
“ok” : 1
}
          (2).这个插入分片数据后的信息:
mongos> db.books.stats();
{
“sharded” : true,
“paddingFactorNote” : “paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only.”,
“userFlags” : 1,
“capped” : false,
“ns” : “test2.books”,
“count” : 20000,
“numExtents” : 11,
“size” : 2240000,
“storageSize” : 5595136,
“totalIndexSize” : 1267280,
“indexSizes” : {
          “_id_” : 678608,
“id_1” : 588672

},
“avgObjSize” : 112,
“nindexes” : 2,
“nchunks” : 5,
“shards” : {
“share_01” : {
“ns” : “test2.books”,
    “count” : 10028,
“size” : 1123136,
“avgObjSize” : 112,
“numExtents” : 5,
“storageSize” : 2793472,
“lastExtentSize” : 2097152,
“paddingFactor” : 1,
“paddingFactorNote” : “paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only.”,
“userFlags” : 1,
“capped” : false,
“nindexes” : 2,
“totalIndexSize” : 629552,
“indexSizes” : {
“_id_” : 335216,
“id_1” : 294336
},
“ok” : 1,
“$gleStats” : {
“lastOpTime” : Timestamp(0, 0),
“electionId” : ObjectId(“5551b02945399f93109d3a39”)
}
},
“share_02” : {
“ns” : “test2.books”,
    “count” : 9964,
“size” : 1115968,
“avgObjSize” : 112,
“numExtents” : 5,
“storageSize” : 2793472,
“lastExtentSize” : 2097152,
“paddingFactor” : 1,
“paddingFactorNote” : “paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only.”,
“userFlags” : 1,
“capped” : false,
“nindexes” : 2,
“totalIndexSize” : 621376,
“indexSizes” : {
“_id_” : 335216,
“id_1” : 286160
},
“ok” : 1,
“$gleStats” : {
“lastOpTime” : Timestamp(0, 0),
“electionId” : ObjectId(“5551af01cfe537190558b164”)
}
},
“share_03” : {
“ns” : “test2.books”,
        “count” : 8,
“size” : 896,
“avgObjSize” : 112,
“numExtents” : 1,
“storageSize” : 8192,
“lastExtentSize” : 8192,
“paddingFactor” : 1,
“paddingFactorNote” : “paddingFactor is unused and unmaintained in 3.0. It remains hard coded to 1.0 for compatibility only.”,
“userFlags” : 1,
“capped” : false,
“nindexes” : 2,
“totalIndexSize” : 16352,
“indexSizes” : {
“_id_” : 8176,
“id_1” : 8176
},
“ok” : 1,
“$gleStats” : {
“lastOpTime” : Timestamp(0, 0),
“electionId” : ObjectId(“5551af39bc340be250ae0cb4”)
}
}
},
“ok” : 1
}
     # 注:
          从上面看,我们这个插入的数据,分配很不均衡,至于何解,有待研究
     3.集群分片信息的查看:
mongos> sh.status();
— Sharding Status —
sharding version: {
“_id” : 1,
“minCompatibleVersion” : 5,
“currentVersion” : 6,
“clusterId” : ObjectId(“5551bfe62d6d99e8bbbc8840”)
}
shards:
{  “_id” : “share_01”,  “host” : “share_01/172.16.10.54:22001,172.16.10.55:22001” }
{  “_id” : “share_02”,  “host” : “share_02/172.16.10.54:22002,172.16.10.55:22002” }
{  “_id” : “share_03”,  “host” : “share_03/172.16.10.54:22003,172.16.10.55:22003” }
balancer:
Currently enabled:  yes
Currently running:  no
Failed balancer rounds in last 5 attempts:  0
Migration Results for the last 24 hours:
3 : Success
1 : Failed with error ‘migration already in progress’, from share_01 to share_02
databases:
{  “_id” : “admin”,  “partitioned” : false,  “primary” : “config” }
{  “_id” : “test”,  “partitioned” : false,  “primary” : “share_01” }
{  “_id” : “test2”,  “partitioned” : true,  “primary” : “share_01” }
test2.books
shard key: { “id” : 1 }
chunks:
share_01     2
share_02     2
share_03     1
{ “id” : { “$minKey” : 1 } } –>> { “id” : 2 } on : share_02 Timestamp(2, 0)
{ “id” : 2 } –>> { “id” : 10 } on : share_03 Timestamp(3, 0)
{ “id” : 10 } –>> { “id” : 4691 } on : share_01 Timestamp(4, 1)
{ “id” : 4691 } –>> { “id” : 10038 } on : share_01 Timestamp(3, 3)
{ “id” : 10038 } –>> { “id” : { “$maxKey” : 1 } } on : share_02 Timestamp(4, 0)

MongoDB 特性:副本集

一、Mongodb的主从模式种类:

     1.master/slave模式(这种结构无法实现自动 failover,MongoDB 官方已经不建议使用主从模式)
     2.Replicat sets模式
二、Mongodb Replicat sets模式 简介:
     MongoDB官方已经不建议使用主从模式了,替代方案是采用副本集的模式。
     主从模式其实就是一个单副本的应用,没有很好的扩展性和容错性。而副本集具有多个副本保证了容错性,就算一个副本挂掉了还有很多副本存在,并且解决了上面第一个问题“主节点挂掉了,整个集群内会自动切换”。
     其架构图如下:
1=故障后=>
2     由图可以看到,客户端连接到整个副本集,不关心具体哪一台机器是否挂掉。
     主服务器负责整个副本集的读写,副本集定期同步数据备份,一但主节点挂掉,副本节点就会选举一个新的主服务器,这一切对于应用服务器不需要关心。
     副本集中的副本节点,在主节点挂掉后,通过心跳机制检测到后,就会在集群内发起主节点的选举机制,自动选举一位新的主服务器。
     注:
          使用副本集时,如果 PRIMARY 挂掉,SECONDARY 会成为 PRIMARY, Mongodb 的访问地址是会改变的。
          但是,Mongodb 提供了各种开发语言访问 Mongodb replica sets 的驱动程序,所以,访问地址的高可用在客户端访问代码中实现。
三、Mongodb Replicat sets 部署规划:
          172.16.10.55:27017     (mongodb master)
          172.16.10.56:27017     (mongodb slave)
          172.16.10.56:27018     (mongodb 仲裁点)
四、Mongodb Replicat sets 配置文件修改:
     1.修改172.16.10.55:27017(mongodb master) 的配置文件如下:
          vim /usr/local/mongodb/conf/mongodb.conf
               port=27017
               dbpath=/usr/local/mongodb/data
               logpath=/usr/local/mongodb/log/mongodb.log
               logappend=true
               fork=true
               oplogSize=4096
               replSet=haowu/172.16.10.56:27017,172.16.10.56:27018
     2.修改172.16.10.56:27017(mongodb slave) 的配置文件如下:
          vim /usr/local/mongodb/conf/mongodb.conf
               port=27017
               dbpath=/usr/local/mongodb/data
               logpath=/usr/local/mongodb/log/mongodb.log
               logappend=true
               fork=true
               oplogSize=4096
               replSet=haowu/172.16.10.55:27017,172.16.10.56:27018
     3.修改172.16.10.56:27018(mongodb 仲裁点) 的配置文件如下:
          vim /usr/local/mongodb_arbirer/conf/mongodb.conf
               port=27018
               dbpath=/usr/local/mongodb_arbirer/data
               logpath=/usr/local/mongodb_arbirer/log/mongodb.log
               logappend=true
               fork=true
 
     # oplogSize:
               用于指定复制时的日志大小,默认大小是磁盘剩余空间的5%,其实就是一个缓存,缓存PRIMARY产生的日志。
     # replSet:
               设置副本集的命令,名字后跟其他的实例地址。实例地址也可以不指定或者指定一个,mongodb可以自动去发现其他节点。为了使配置清晰,指定了节点地址。
五、Mongodb Replicat sets 启动:
      1.172.16.10.55:27017(mongodb master)启动:
          /usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/mongodb.conf
      2.172.16.10.56:27017(mongodb slave)启动:
         /usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/mongodb.conf
      3.172.16.10.56:27018(mongodb 仲裁点)启动:
          /usr/local/mongodb_arbirer/bin/mongod –config /usr/local/mongodb_arbirer/conf/mongodb.conf
          # 在启动集群后,需要对集群做初始化配置,初始化配置需要所有的节点都在启动状态
六、Mongodb Replicat sets 集群初始化:
     登录到172.16.10.55:27017(mongodb master)上,执行如下操作:
      /usr/local/mongodb/bin/mongo>config_rs1={_id:’haowu’,members:[
                                                                      {_id:0,host:’172.16.10.55:27017′,priority:10},
                                                                      {_id:1,host:’172.16.10.56:27017′,priority:9},
                                                                      {_id:2,host:’172.16.10.56:27018′,”arbiterOnly”:true}
                                                                      ]}
        /usr/local/mongodb/bin/mongo>rs.initiate(config_rs1)
        /usr/local/mongodb/bin/mongo>rs.status()
        /usr/local/mongodb/bin/mongo>rs.config()
        /usr/local/mongodb/bin/mongo>rs.initiate(config_rs1)
七、Mongodb Replicat sets 同步测试:
      1. 登录 mongodb master 上,插入数据:
          haowu:PRIMARY> show collections;
haowu:PRIMARY> db.haowu.insert({name:”haowu”,age:28})
haowu:PRIMARY> db.haowu.find();
      2. 登录 mongodb slave 上,执行如下操作,看是否同步:
          # mongodb默认是从主节点读写数据的,副本节点上不允许读,下面,设置副本节点可读
          haowu:SECONDARY> rs.slaveOk()
haowu:SECONDARY> db.haowu.find();

八、Mongodb Replicat sets 故障切换测试:
      1. 登录 mongodb master 上,关闭master:
          haowu:PRIMARY> use admin、
haowu:PRIMARY> db.shutdownserver();
      2. 登录 mongodb slave 上,查看slave日志切换信息:
          其实,登录slave,你会发现slave的标识由:haowu:SECONDARY>  变为  haowu:PRIMARY>
          haowu:SECONDARY> rs.isMaster()
      3.我们这里,因为设置了副本集成员的优先级,所以,当宕机的master 重新启动后,会自动再次成为 master;
         因此,如果想避免此类事件发生,我们可以去掉优先级设置
九、Mongodb Replicat sets 读写分离配置:
     读写分离,将读压力分散到副本集的副本节点上,可以减轻主节点的读写压力
     (1)设置读写分离,需要先在副本节点SECONDARY ,设置 setSlaveOk。
     (2)在程序中设置副本节点负责读操作,

MongoDB 单实例部署 [ 非源码编译安装 ]

MongoDB 官方网址:
http://www.mongodb.org

本文,主要介绍 MongoDB 单实例的安装部署~

1.下载MongoDB的安装包并解压,安装相关依赖包 [ 这里不采用源码编译安装 ]
yum -y install net-snmp.x86_64 net-snmp-devel.x86_64 net-snmp-libs.x86_64
mkdir /usr/local/mongodb
tar -xf mongodb-linux-x86_64-enterprise-rhel62-3.0.2.tgz
mv mongodb-linux-x86_64-enterprise-rhel62-3.0.2 /usr/local/mongodb

2. 创建数据目录,日志目录,配置目录等相关目录
cd /usr/local/mongodb
mkdir data conf log

3.编辑MongoDB 的配置文件:
vim conf/mongodb.conf
port=27017
dbpath=/usr/local/mongodb/data
logpath=/usr/local/mongodb/log/mongodb.log
logappend=true
fork=true

4.启动/关闭MongoDB:
(1).启动MongoDB:
/usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/mongodb.conf

(2).关闭MongoDB:
/usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/mongodb.conf –shutdown

(3).主从启动的相关要点: [也可将其参数写入配置文件 master=true ]
A. 如果mongodb 做为 master 的话,只要在启动时,加 –master 参数即可:
ex:
/usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/mongodb.conf –master
# 同时,查看日志,日志会有相关master 的信息

B. 如果mongodb 做为 SLAVE 的话,只要在启动时,加 –slave –source ip:端口 参数即可:
ex:
/usr/local/mongodb/bin/mongod –config /usr/local/mongodb/conf/mongodb.conf –slave –source 172.16.10.55:27017
# 同时,查看日志,日志会有相关slave 的信息

C.主从数据源信息的变更:
> db.printSlaveReplicationInfo();
source: 172.16.10.5627017
doing initial sync
> use local
> db.sources.find();
{ “_id” : ObjectId(“5549957a542399f1888879b2”), “host” : “172.16.10.5627017”, “source” : “main” }
> db.sources.insert({“host”: “172.16.10.56:27017”}); # 添加一个新的主数据源
WriteResult({ “nInserted” : 1 })
> db.sources.find();
{ “_id” : ObjectId(“5549957a542399f1888879b2”), “host” : “172.16.10.5627017”, “source” : “main” }
{ “_id” : ObjectId(“5549c25670656588c7058016”), “host” : “172.16.10.56:27017” }
> db.sources.remove({“host”: “172.16.10.5627017”}); # 删除一个已存在的数据源
WriteResult({ “nRemoved” : 1 })
> db.sources.find();
{ “_id” : ObjectId(“5549c25670656588c7058016”), “host” : “172.16.10.56:27017” }

D.查看主从复制信息:
> db.printReplicationInfo();
configured oplog size: 1550MB
log length start to end: 72993secs (20.28hrs)
oplog first event time: Tue May 05 2015 17:27:01 GMT+0800 (CST)
oplog last event time: Wed May 06 2015 13:43:34 GMT+0800 (CST)
now: Wed May 06 2015 14:24:21 GMT+0800 (CST)
> db.printSlaveReplicationInfo();
source: 172.16.10.55:27017
syncedTo: Wed May 06 2015 14:24:24 GMT+0800 (CST)
13 secs (0 hrs) behind the freshest member (no primary available at the moment)

5.注意事项:
启动后,日志信息如下,红色警告部分,是我们需要进行调整的地方:
2015-05-05T15:14:48.310+0800 I JOURNAL [initandlisten] journal dir=/usr/local/mongodb/data/journal
2015-05-05T15:14:48.311+0800 I JOURNAL [initandlisten] recover : no journal files present, no recovery needed
2015-05-05T15:14:48.368+0800 I JOURNAL [durability] Durability thread started
2015-05-05T15:14:48.368+0800 I JOURNAL [journal writer] Journal writer thread started
2015-05-05T15:14:48.369+0800 I CONTROL [initandlisten] MongoDB starting : pid=8874 port=27017 dbpath=/usr/local/mongodb/data 64-bit host=Templet
2015-05-05T15:14:48.369+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2015-05-05T15:14:48.369+0800 I CONTROL [initandlisten]
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten]
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is ‘always’.
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] ** We suggest setting it to ‘never’
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten]
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is ‘always’.
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] ** We suggest setting it to ‘never’
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten]
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] db version v3.0.2
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] git version: 6201872043ecbbc0a4cc169b5482dcf385fc464f modules: enterprise
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] OpenSSL version: OpenSSL 1.0.1e-fips 11 Feb 2013
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] build info: Linux ip-10-171-117-53 2.6.32-220.el6.x86_64 #1 SMP Wed Nov 9 08:03:13 EST 2011 x86_64 BOOST_LIB_VERSION=1_49
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] allocator: tcmalloc
2015-05-05T15:14:48.370+0800 I CONTROL [initandlisten] options: { config: “/usr/local/mongodb/conf/mongodb.conf”, net: { port: 27017 }, processManagement: { fork: true }, storage: { dbPath: “/usr/local/mongodb/data” }, systemLog: { destination: “file”, logAppend: true, path: “/usr/local/mongodb/log/mongodb.log” } }
2015-05-05T15:14:48.372+0800 I INDEX [initandlisten] allocating new ns file /usr/local/mongodb/data/local.ns, filling with zeroes…
2015-05-05T15:14:48.435+0800 I STORAGE [FileAllocator] allocating new datafile /usr/local/mongodb/data/local.0, filling with zeroes…
2015-05-05T15:14:48.435+0800 I STORAGE [FileAllocator] creating directory /usr/local/mongodb/data/_tmp
2015-05-05T15:14:48.439+0800 I STORAGE [FileAllocator] done allocating datafile /usr/local/mongodb/data/local.0, size: 64MB, took 0 secs
2015-05-05T15:14:48.450+0800 I NETWORK [initandlisten] waiting for connections on port 27017

下面,我们进行调整:
(1).创建mongodb用户
注:Mongodb不建议使用root用户来运行
# groupadd -g 1010 mongodb
# useradd -g 1010 -u 1010 mongodb
调整目录和文件权限
# cd /usr/local/mongodb
# chown -R mongodb. data/ log/

(2).内核参数调整
调整前:
# cat /sys/kernel/mm/transparent_hugepage/enabled
[always] never
# cat /sys/kernel/mm/transparent_hugepage/defrag
[always] never
调整方法:
# echo “never” > /sys/kernel/mm/transparent_hugepage/enabled
# echo “never” > /sys/kernel/mm/transparent_hugepage/defrag
调整后:
# cat /sys/kernel/mm/transparent_hugepage/enabled
always [never]
# cat /sys/kernel/mm/transparent_hugepage/defrag
always [never]

(3).调整限制参数
启动日志,提示启动mongodb的这个用户能打开的进程数不到能打开的文件数的二分之一;
建议调整该用户能打开进程数为能打开的文件数的二分之一以上;
在CentOS系统中,默认情况用户能打开的进程数和文件数都为1024,因此不会出现该提示;
至于限制参数可以通过ulimit命令和/etc/security/limits.conf来设置
设置完毕后重启服务(切换到 mongodb 用户,启动mongodb)

MongoDB的日常使用

一、 查看collection和db信息:
1.查看DB以及DB空间大小:
show dbs
2.选择DB:
use hoss
3.查看DB中的collections:
show collections

二、查看collection使用大小:
1.查看DB给collection分配的大小。相当于oracle的segment大小:
db.fs.files.storageSize()
2.查看collection实际使用的大小。相当于oracle的HWR之下的:
db.fs.files.dataSize()
block,即已使用的block
3.查看collection的大小(storageSize)与索引大小的总和:
db.fs.files.totalSize()
4.查看collection的详细信息:
db.fs.files.stats()

三、 删除collection和db:
1.删除当前数据库:
db.dropDatabase()
2.删除当前collection的索引:
db.mycoll.dropIndexes()
3.删除当前collection
db.mycoll.drop()

四、检查同步状态
db.printReplicationInfo()
db.printSlaveReplicationInfo()
haowu:SECONDARY> db.printReplicationInfo()
configured oplog size: 4096MB
log length start to end: 449727secs (124.92hrs)
oplog first event time: Thu Dec 11 2014 11:00:40 GMT+0800 (CST)
oplog last event time: Tue Dec 16 2014 15:56:07 GMT+0800 (CST)
now: Tue Dec 16 2014 15:56:57 GMT+0800 (CST)

haowu:SECONDARY> db.printSlaveReplicationInfo()
source: 172.16.10.40:27017
syncedTo: Tue Dec 16 2014 15:56:07 GMT+0800 (CST)
= 55 secs ago (0.02hrs)
source: 172.16.10.22:27017
no replication info, yet. State: ARBITER

只要 oplog last event time 和 syncedTo 一致,就代表没有同步延迟

五、备份和恢复操作:
1.备份:mongodump
mongodump使用的是普通的查询机制(无锁定),所以产生的备份不一定是服务器数据的实时快照,即当运行备份同时有客户端写入数据,该数据就不会备份到磁盘,备份时的查询会对其他客户端的性能产生不利的影响。即可对运行的数据库使用,也可对未运行的数据使用。
如果想要获取一致的备份,可以使用命令 db.fsyncLock() 给数据库加锁,阻塞写操作,备份结束后使用命令 db.fsyncUnlock() 解锁。
例:
备份数据库:
mongodump -d test -o ./backup

2.恢复:mongorestore
使用 mongorestore 命令来恢复数据,数据源是 mongodump 命令产生的文件
恢复所有数据:
./mongorestore –drop /opt/backup/mongodb
恢复数据库:
./mongorestore -d test –drop /opt/backup/mongodb/test/
恢复集合:
./mongorestore -d test -c wu –drop /opt/backup/mongodb/test/wu.bson

六、导入和导出操作:
1.导出:mongoexport
使用mongoexport命令导出的数据是文本模式,用户可读的。
导出的粒度就是集合,也只是集合,不能导出某个数据库。
导出的数据格式可以有三种:json,csv,jsonArray。
使用最多的就是json和csv。
例:
mongoexport –port 27018 -d test -c test -o test.json

2.导入:mongoimport
mongoimport命令也只能导入集合。
默认导入json格式的文件。
如果是csv,需要使用–type来指定,并配合–headerline参数。
例:
mongoimport -d test -c test –drop –type csv –headerline –file ~/test.csv

twemproxy 简介、安装配置

官方地址:
https://github.com/twitter/twemproxy

一、Twemproxy简介及其特性、缺点:
1.Twemproxy简介:
Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII协议和更新的Redis协议。它全部用C写成,使用Apache 2.0 License授权。
项目在Linux上可以工作,而在OSX上无法编译,因为它依赖了epoll API.
twemproxy 主要用于Redis 和 Memcached 的集群代理

2.Twemproxy特性:
速度快
轻量级
维护持久的服务器连接
Keeps connection count on the backend caching servers low.
启用请求和响应的管道
支持代理到多个服务器
同时支持多个服务器池
多个服务器自动分享数据
实现了完整的 memcached ascii 和 redis 协议.
服务器池配置简单,通过一个 YAML 文件即可
Supports multiple hashing modes including consistent hashing and distribution.
可配置在失败时禁用某个节点
Observability through stats exposed on stats monitoring port.
支持 Linux, *BSD, OS X and Solaris (SmartOS)

3.Twemproxy不足之处:
不支持针对多个值的操作,比如取sets的子交并补等(MGET 和 DEL 除外)
不支持Redis的事务操作
出错提示还不够完善
也不支持select操作
虽然可以动态移除节点,但该移除节点的数据就丢失了
redis集群动态增加节点的时候,twemproxy不会对已有数据做重分布。maillist里面作者说这个需要自己写个脚本实现性能上的损耗

二、Twemproxy部署:
1.Twemproxy 编译安装:
autoconf下载地址: http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz
twemproxy下载地址:https://codeload.github.com/twitter/twemproxy/zip/master
twemproxy的安装要求autoconf的版本在2.64以上,否则提示”error: Autoconf version 2.64 or higher is required“
twemproxy的安装方法与git上README.md里面的过程基本相同,只是configure过程中多了–prefix选项,将工具安装到指定目录
(1).autoconf的安装:
tar -xf autoconf-2.69.tar.gz
cd autoconf-2.69
./configure
make
make install
# 测试是否安装成功:
autoconf –version

(2).twemproxy的安装:
unzip twemproxy-master.zip
cd twemproxy-master
autoreconf -fvi
./configure –prefix=/usr/local/twemproxy
make -j 2
make install

2.Twemproxy 配置文件:
cd /usr/local/twemproxy/
mkdir run conf
vim conf/nutcracker.yml
redis:
listen: 172.16.10.54:6666 # 使用哪个端口启动Twemproxy
redis: true # 是否是Redis的proxy
hash: fnv1a_64 # 指定具体的hash函数
distribution: ketama # 具体的hash算法
auto_eject_hosts: true # 是否在结点无法响应的时候临时摘除结点
timeout: 400 # 超时时间(毫秒)
server_retry_timeout: 3000 # 重试的时间(毫秒)
server_failure_limit: 1 # 结点故障多少次就算摘除掉
servers: # 下面表示所有的Redis节点(IP:端口号:权重)
– 172.16.10.55:6379:1
– 172.16.10.56:6379:1

3.Twemproxy 配置文件测试,命令生效,服务启动:
(1).命令生效:
vim ~/.bash_profile
PATH=$PATH:$HOME/bin:/usr/local/keepalived/sbin:/usr/local/twemproxy/sbin
. ~/.bash_profile # 使命令生效
(2).配置文件测试:
nutcracker -t
nutcracker: configuration file ‘conf/nutcracker.yml’ syntax is ok # 显示此信息,说明配置文件正常
(3).Twemproxy服务启动:
nutcracker -d -c /usr/local/twemproxy/conf/nutcracker.yml -p /usr/local/twemproxy/run/redisproxy.pid -o /usr/local/twemproxy/run/redisproxy.log
(4).Twemproxy配置文件书写需要注意的问题:
A.yml配置文件中每个参数值对分隔符”:”后,需要有一个空格;
B.不同层次的参数需要缩进区分,最好使用tab键缩进,否则nutcracker进程不能启动;
C.在auto_eject_hosts: true的时候,关闭一个redis实例后,写入数据还是提示“(error) ERR Connection refused”
这个与server_retry_timeout参数设置太小有关,默认值30000msec是一个很好的选择。

4.Twemproxy 启动、停止脚本编写:
vim /etc/init.d/twemproxy
#!/bin/bash
#chkconfig: 2345 10 90
#description:twemproxy

function Stop()
{
/usr/bin/pkill nutcracker
}
function Start()
{
nutcracker -d -c /usr/local/twemproxy/conf/nutcracker.yml -p /usr/local/twemproxy/run/redisproxy.pid -o /usr/local/twemproxy/run/redisproxy.log
}
case $1 in
stop)
Stop
;;
start)
Start
;;
restart)
Stop
Start
;;
*)
echo “Usage: `basename $0` {stop|start}”
;;
esac
# 添加twemproxy 服务:
chkconfig –add twemproxy

三、Twemproxy测试:
redis-cli -h 172.16.10.54 -p 6666
172.16.10.54:6666> select 0 # 不支持select
Error: Server closed the connection
172.16.10.54:6666> get name
“Michael.xu”
172.16.10.54:6666> set names “wu.zhong”
OK
172.16.10.54:6666> get names
“wu.zhong”

# 性能测试:
redis-benchmark -h 172.16.10.54 -p 6666 -c 100 -t set -d 100 -l -q

Redis HA架构:Redis(master/slave) + Keepalived + Redis Sentinel + Twemproxy + Twemproxy Agent

一、Redis HA架构描述:
目前,Redis高可用方案,主要集中于以下几种:
1.Redis(master/slave)
2.Redis(master/slave) + Keepalived + Redis Sentinel
3.Codis
4.Redis(master/slave) + Keepalived + Redis Sentinel + Twemproxy + Keepalived
5.Redis(master/slave) + Keepalived + Redis Sentinel + Twemproxy + Twemproxy Agent + nodejs +npm
第 4 种,我们通过 两个keepalived 来是实现HA,一个是实现Redis HA的vip ;一个是Twemproxy HA的VIP
第 5 种,使用一个 keepalived 来实现Twemproxy HA,通过 Twemproxy Agent 来更新 Twemproxy 配置(当redis主从切换时)
下面,我们主要讲解第 5 种 方案:
Redis(master/slave) + Keepalived + Redis Sentinel + Twemproxy + Twemproxy Agent + nodejs + nodejs-forever-agent + npm

二、Redis HA架构图:

Image

三、Redis(master/slave) + Redis Sentinel 部署:
具体的安装可以参照:
Redis HA 下的
20141202:Redis 主从部署
20150421:Redis Sentinel + keepalived 部署
下面,将简单描述:
1.Redis(master/slave) 的部署:[ 部署在 10.55、10.56 ]
10.55、10.56 都部署两套Redis,
# 第二套redis,只要将配置文件( redis.conf) 复制并改名为 redis_6380.conf
# 新配置文件/ 启动脚本等 修改,参照 Redis 主从部署一篇
2.Redis Sentinel 部署:[ 部署在 10.54、10.55、10.56 ]
# 只要在Redis Sentinel 的配置文件,把新建的Redis master 添加进去,就可;
# 具体,参照如下文件:
vim /usr/local/redis-sentinel/etc/sentinel.conf
port 26379
dir /usr/local/redis-sentinel/data
sentinel monitor mymaster 172.16.10.55 6379 1
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 60000
# sentinel client-reconfig-script mymaster /usr/local/redis-sentinel/bin/keepalived.sh # 这里我们在故障切换时,就不再执行脚本
sentinel monitor mymaster2 172.16.10.56 6380 1
sentinel down-after-milliseconds mymaster2 5000
sentinel parallel-syncs mymaster2 1
sentinel failover-timeout mymaster2 60000
3.下面启动相关Redis服务:
(1).启动 Redis服务;
(2).启动 Redis Sentinel 服务;
(3).测试 Redis master/slave 切换情况;

四、keepalived 部署:[ 部署在 10.53、10.54 ]
# keepalived 部署 参照 ”20150421:Redis Sentinel + keepalived 部署 “ 文章中的 keepalived 部署 一段
# 在此,就不再赘述

五、Twemproxy 部署:[ 部署在 10.53、10.54 ]
# Twemproxy 部署 参照 “Redis 中间件” 中的 “20150427:twemproxy 简介、安装配置”文章
# 在此,就不再赘述

六、Twemproxy Agent 部署:[ 部署在 10.53、10.54 ]
1.安装nodejs + npm:
yum -y install nodejs npm
2.安装forever:
npm install forever -g
3.安装Twemproxy Agent
(1).解压Twemproxy Agent 包,并复制到指定目录:
mv /tmp/redis-twemproxy-agent-master /usr/local/redis-twemproxy-agent
(2).修改redis-twemproxy-agent/lib/cli.js文件:
修改cli.parse里的文件路径为部署的路径,具体参照如下配置:
cli.parse
({
host: [‘h’, ‘Redis sentinel hostname’, ‘string’, ‘172.16.10.54’],
port: [‘p’, ‘Redis sentinel port number’, ‘number’, 26379],
config: [‘f’, ‘Path to twemproxy config’, ‘path’, ‘/usr/local/twemproxy/conf/nutcracker.yml’],
command: [‘c’, ‘Command to restart twemproxy’, ‘string’, ‘/etc/init.d/twemproxy restart’],
log: [‘l’, ‘The log file location’, ‘string’, ‘/usr/local/twemproxy/run/twemproxy_sentinel.log’]
});
(3).将 redis-twemproxy-agent/init.d/twemproxy_sentinel 复制到 /etc/init.d/ 目录下:
cp /usr/local/redis-twemproxy-agent/init.d/twemproxy_sentinel /etc/init.d/

# 修改该脚本redis-twemproxy-agent,nodejs,forever,的路径为实际路径,参考如下:
ROOT=/usr/local/redis-twemproxy-agent
EXEC=$ROOT/bin/redis_twemproxy_agent
NODE=/usr/bin/node
FOREVER=/usr/bin/forever
(4).启动 redis-twemproxy-agent 服务:
/etc/init.d/twemproxy_sentinel start

# 此测试结果,没有能证明 redis-twemproxy-agent 能自动修改 twemproxy 的配置文件,可能在文件配置方面有误,后续再研究

Redis Sentinel + keepalived 部署

一、Redis Sentinel简介:
Redis Sentinel 是一个分布式系统,你可以在一个架构中运行多个Sentinel 进程(progress),这些进程使用流言协议(gossipprotocols) 来接收关于主服务器是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。
虽然Redis Sentinel只是一个单独的可执行文件 redis-sentinel,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 –sentinel 选项来启动 Redis Sentinel。
源码编译安装后,src目录有一个命名为redis-sentinel的程序。
对于redis-sentinel 程序,你可以用以下命令来启动Sentinel 系统:redis-sentinel /path/to/sentinel.conf

二、Redis Sentinel 的部署:
1.复制 redis 目录为 Redis Sentinel
# 这里我们基于原先 源码编译安装的redis目录,复制一个副本,改名为: Redis Sentinel
cp -r /usr/local/redis-3.0 /usr/local/redis-sentinel

2.正常安装:我们需要建 etc、bin、data、log 等目录;
但是,因为我们是复制redis目录,这写目录都有了,我们只需要稍作改动:
# 在 10.54、10.55 、 10.56 的redis上,都配置此文件,只是在 10.54[ 此机器上没有装redis ]上的配置文件 sentinel client-reconfig-script 注释掉
vim /usr/local/redis-sentinel/etc/sentinel.conf
port 26379
dir /usr/local/redis-sentinel/data
sentinel monitor mymaster 172.16.10.55 6379 1
sentinel down-after-milliseconds mymaster 5000 # 单位为毫秒
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 60000
sentinel client-reconfig-script mymaster /usr/local/redis-sentinel/bin/keepalived.sh # 检测故障后,调用的keepalive脚本,需要有X权限

3.部署redis-sentinel 启动脚本:
vim /usr/local/redis-sentinel/bin/startup.sh
#!/bin/bash
CMD_PATH=/usr/local/redis-sentinel/bin/redis-sentinel
CONF_PATH=/usr/local/redis-sentinel/conf/sentinel.conf
LOG_PATH=/usr/local/redis-sentinel/log/sentinel.log
${CMD_PATH} ${CONF_PATH} >> ${LOG_PATH} 2>&1 &

以下为参考脚本,暂不使用:
#!/bin/bash
#Configurations injected by install_server below….
EXEC=/usr/local/redis-sentinel/bin/redis-sentinel
CLIEXEC=/usr/local/redis-sentinel/bin/redis-cli
PIDFILE=/usr/local/redis-sentinel/log/redis-sentinel_26379.pid
CONF=”/usr/local/redis-sentinel/etc/sentinel.conf”
REDISPORT=”26379″
case “$1” in
start)
if [ -f $PIDFILE ]
then
echo “$PIDFILE exists, process is already running or crashed”
else
echo “Starting Redis server…”
$EXEC $CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo “$PIDFILE does not exist, process is not running”
else
PID=$(cat $PIDFILE)
echo “Stopping …”
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo “Waiting for Redis to shutdown …”
sleep 1
done
echo “Redis stopped”
fi
;;
status)
PID=$(cat $PIDFILE)
if [ ! -x /proc/${PID} ]
then
echo ‘Redis is not running’
else
echo “Redis is running ($PID)”
fi
;;
restart)
$0 stop
$0 start
;;
*)
echo “Please use start, stop, restart or status as first argument”
;;
esac

三、keepalived 部署:
1.keepalive的安装,安装目录为/usr/local/keepalive
# 默认软件已上传并且解压,进入keepalived目录
./configure –prefix=/usr/local/keepalived
make && make install

# 在配置过程中可能出现某些软件包没有正确安装的提示,如openssl,popt等。
解决办法是使用 yum install openssl*以及yum install popt popt-deve l来安装缺少的软件包。
Keepalived 安装完成后,会在安装目录/usr/local/keepalived 生成 bin,etc,man,sbin 这4 个目录。其中etc为配置文件所在的目录。

2.配置 keepalived 环境变量
# 给root用户增加环境变量,在export PATH行之前加入一行:
vi ~/.bash_profile
PATH=$PATH:/usr/local/keepalived/sbin
保存退出,执行下面命令,使参数生效
. ~/.bash_profile

3.整理相关文件和命令,方便使用service 启动
cp -a /usr/local/keepalived/sbin/keepalived /usr/sbin/
cp -a /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
cp -a /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/
# 让chkconfig管理新添加的服务
chkconfig –add keepalived
# 添加启动级别
chkconfig keepalived on
# 查看启动设置
chkconfig –list keepalived

4.keepalived配置文件的修改如下:配置文件目录在( /etc/keepalived/keepalived.conf )
# 172.16.10.55 master 的配置文件
global_defs {
notification_email
{
13501960667@139.com # 收件邮箱
}
notification_email_from mysql_lab@localhost # 发件邮箱
smtp_server localhost # 发件服务器
smtp_connect_timeout 30 # 超时时间
router_id HAOWU_55
}
vrrp_sync_group VG1 {
group
{
VI_1
}
}
vrrp_instance VI_1 {
state MASTER
# state BACKUP
# nopreempt
interface eth0
virtual_router_id 96
priority 110
advert_int 1
authentication
{
auth_type PASS
auth_pass Pa$$w0rd
}
virtual_ipaddress
{
172.16.10.251
}
}

# 172.16.10.56 slave 的配置文件
global_defs {
notification_email
{
13501960667@139.com
}
notification_email_from mysql_lab@localhost
smtp_server localhost
smtp_connect_timeout 30
router_id HAOWU_56
}
vrrp_sync_group VG1 {
group
{
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 96
priority 100
advert_int 1
authentication
{
auth_type PASS
auth_pass Pa$$w0rd
}
virtual_ipaddress
{
172.16.10.251
}
}

注:
# 红色部分 是在我们实际配置中,需要修改的地方;
# 紫红色部分 是我们在HA操作中,可以参考的设置下:
具体理解如下:
主/从的state 都设置为: BACKUP ,同是将 priority( 110>100 ) 大的机器设置为 nopreempt 可以避免切换后,vip抢占问题;
nopreempt 只能工作于 state 为BACKUP 的状态下;

5.编写keepalived 切换脚本(55、56上配置此脚本):
vim /usr/local/redis-sentinel/bin/keepalived.sh
# 红色加粗脚本,只适用于主机宕机
#!/bin/bash
REDISCLI=”/usr/local/redis-3.0/bin/redis-cli”
if $REDISCLI info|grep role|grep -q master ;then
echo “start keepalived” >> ../log/sentinel.log
/etc/init.d/keepalived start >> ../log/sentinel.log
else
echo “stop keepalived” >> ../log/sentinel.log
/etc/init.d/keepalived stop >> ../log/sentinel.log
fi

# 下面脚本为改进脚本:
#!/bin/bash
EXEC=/usr/local/redis-3.0/bin/redis-server
CLIEXEC=/usr/local/redis-3.0/bin/redis-cli
CONF=”/usr/local/redis-3.0/etc/redis.conf”
REDISPORT=”6379″
KEEP_PID=`ps -A | grep keepalived |awk ‘{print $1}’ > /dev/null`

$CLIEXEC -p $REDISPORT info > /dev/null
if [ $? -eq 0 ]; then
if [ -z “$KEEP_PID” ]; then
# try to start keepalived again
/etc/init.d/keepalived start
else
echo “ERROR!!! server:keepalved is down!” | mail -s “ERROR!! keepalived is down !!!” 13501960667@139.com
fi
else
# try to start it again
# 以下部分 为了测试需要需要注释掉[ 下面这段脚本是判断能否重启redis,这里为测试需要,注释掉 ]
# $EXEC $CONF
# $CLIEXEC -p $REDISPORT info > /dev/null
# if [ $? -eq 0 ]; then
# exit 0
# else
# restart failed
/etc/init.d/keepalived stop
# fi
fi

# 给予脚本 执行权限:
chmod +x /usr/local/redis-sentinel/bin/keepalived.sh

四、部署测试:
1.测试keepalived 虚 ip 切换情况:
master(55) 和 slave(56) :
# 启动 keepalived
/etc/init.d/keepalived start
# 执行 ip addr:查看当前虚ip情况(停掉matser 的keepalived 看 虚IP 是否飘到 slave 上)

2.redis-sentinel启动测试:
/usr/local/redis-sentinel/bin/redis-sentinel /usr/local/redis-sentinel/etc/sentinel.conf
# 启动后,会有详细的信息输出,包括当前的主,redis-sentinel的个数,地址等

# 登陆 redis-sentinel 查看相关信息:
bin/redis-cli -h 172.16.10.56 -p 26379
172.16.10.56:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=172.16.10.55:6379,slaves=1,sentinels=3

3.redis 故障测试:
# 将 当前master(172.16.10.55) 关闭,看redis-sentinel 的切换日志
bin/redis-cli -h 172.16.10.55 -p 6379 shutdown
# 切换日志如下:
15296:X 22 Apr 16:38:49.292 # +sdown master mymaster 172.16.10.55 6379
15296:X 22 Apr 16:38:49.292 # +odown master mymaster 172.16.10.55 6379 #quorum 1/1
15296:X 22 Apr 16:38:49.292 # Next failover delay: I will not start a failover before Wed Apr 22 16:40:49 2015
15296:X 22 Apr 16:38:50.313 # +config-update-from sentinel 172.16.10.54:26379 172.16.10.54 26379 @ mymaster 172.16.10.55 6379
15296:X 22 Apr 16:38:50.313 # +switch-master mymaster 172.16.10.55 6379 172.16.10.56 6379
15296:X 22 Apr 16:38:50.313 * +slave slave 172.16.10.55:6379 172.16.10.55 6379 @ mymaster 172.16.10.56 6379
15296:X 22 Apr 16:38:50.441 # -script-error /usr/local/redis-sentinel/bin/keepalived.sh 0 2
15296:X 22 Apr 16:38:55.342 # +sdown slave 172.16.10.55:6379 172.16.10.55 6379 @ mymaster 172.16.10.56 6379

Redis 主从部署

Redis 部署说明:
redis使用两台机器,172.16.10.55,172.16.100.56。
55为redis master,56为redis slave,
监听默认的6379端口。

(1).创建Redis相关目录:
cd /usr/local/redis-3.0
# 如果相关目录已经在安装时,新建,下面步骤就忽略
mkdir -p /usr/local/redis-3.0/{data,log,etc}

(2).编辑Redis Master/Slave 相关配置文件:
vim etc/redis.conf

Master配置文件(生产实际配置文件不是这样,比较简单)
######################## GENERAL ######################
daemonize yes
pidfile /usr/local/redis-3.0/log/redis_6379.pid
port 6379
timeout 0
loglevel notice
logfile “/usr/local/redis-3.0/log/redis_6379.log”
syslog-enabled no
databases 16
#########################SAVE###########################
dbfilename dump.rdb
dir /usr/local/redis-3.0/data
stop-writes-on-bgsave-error no
#########################REPLICATION ###################
slave-serve-stale-data yes
slave-read-only yes
repl-timeout 60
repl-backlog-size 10mb
repl-backlog-ttl 3600
repl-ping-slave-period 10
min-slaves-to-write 0
min-slaves-max-lag 0
##########################SECURITY ######################
########################## LIMITS ########################
maxclients 2000
#########################APPEND ONLY MODE #############
appendonly no
appendfilename “appendonly.aof”
#########################SLOW LOG #######################
slowlog-log-slower-than 10000
slowlog-max-len 1000

**********************************************************************************

Slave配置文件(生产实际配置文件不是这样,比较简单)
######################## GENERAL ######################
daemonize yes
pidfile /usr/local/redis-3.0/log/redis_6379.pid
port 6379
timeout 0
loglevel notice
logfile “/usr/local/redis-3.0/log/redis.log”
syslog-enabled no
databases 16
#########################SAVE###########################
dbfilename dump.rdb
dir /usr/local/redis-3.0/data
stop-writes-on-bgsave-error no
#########################REPLICATION ###################
slaveof 172.16.10.55 6379
slave-serve-stale-data yes
slave-read-only yes
repl-timeout 60
repl-backlog-size 10mb
repl-backlog-ttl 3600
repl-ping-slave-period 10
min-slaves-to-write 0
min-slaves-max-lag 0
##########################SECURITY ######################
########################## LIMITS ########################
maxclients 2000
#########################APPEND ONLY MODE #############
appendonly no
appendfilename “appendonly.aof”
#########################SLOW LOG #######################
slowlog-log-slower-than 10000
slowlog-max-len 1000

(3).启动/关闭Redis服务:
启动Redis服务顺序:
先启动 Redis Master , 再启动 Redis Slave
关闭Redis服务顺序:
先关闭 Redis Master , 再关闭 Redis Slave

(4).验证Redis 主从复制是否正常:
最简单的方式,就是登录 Redis Master,设置一个值:select 0;set name Michael;
然后登录 Redis Slave ,看能不能获取到刚才设置的那个值:select 0;get name;

网上有种验证方式如下:
进入主从服务器的数据目录,查询对比所有服务器数据文件的散列值和文件大小:
find . -type f -name “*.rdb” | xargs md5sum
find . -type f -name “*.rdb” | xargs ls -l

生成报文摘要并验证,如果对比成功则数据已同步:
find . -type f -name “*.rdb” | xargs md5sum >biran
md5sum –check biran

Redis 3.0 编译安装

官方主站:http://www.redis.io/
下载地址:http://www.redis.cn/download.html
Command API: http://www.redis.cn/commands.html
Redis官方文档:http://redis.io/documentation

一.Redis 介绍:
Redis是Remote Dictionary Server的缩写。他本质上一个Key/Value数据库,与Memcached类似的NoSQL型数据库,但是他的数据可以持久化的保存在磁盘上,解决了服务重启后数据不丢失的问题,他的值可以是string(字符串)、list(列表)、sets(集合)或者是ordered sets(被排序的集合),所有的数据类型都具有push/pop、add/remove、执行服务端的并集、交集、两个sets集中的差别等等操作,这些操作都是具有原子性的,Redis还支持各种不同的排序能力。

二.Redis 部署
1.安装相关依赖包:
yum -y install gcc gcc-c++ cmake make

2.安装tcmalloc包[ 尝试采用新的内存分配方式 ]
Redis并没有自己实现内存池,没有在标准的系统内存分配器上再加上自己的东西。
所以系统内存分配器的性能及碎片率会对Redis造成一些性能上的影响。
在最新版本中,jemalloc已经作为源码包的一部分包含在源码包中,所以可以直接被使用。
如果要使用tcmalloc的话,是需要自己安装的。

tcmalloc是google-proftools( http://code.google.com/p/gperftools/downloads/list)中的一部分,
所以我们实际上需要安装google-proftools。如果你是在64位机器上进行安装,需要先安装其依赖的libunwind库,32位的系统不需要
下载地址: http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-alpha.tar.gz

(1).安装libunwind
tar zxvf libunwind-0.99-alpha.tar.gz
cd libunwind-0.99-alpha/
CFLAGS=-fPIC ./configure
make CFLAGS=-fPIC
make CFLAGS=-fPIC install

(2).安装gperftools
tar zxvf gperftools-2.1.tar.gz
cd gperftools-2.1
./configure –disable-cpu-profiler –disable-heap-profiler –disable-heap-checker –disable-debugalloc –enable-minimal
make && make install
cd /usr/local/lib
ln -sv libtcmalloc_minimal.so.4.1.2 libtcmalloc.so
# 加粗部分如果不做的话,后面编译redis会报错: usr/bin/ld: cannot find ltcmalloc
echo “/usr/local/lib” > /etc/ld.so.conf.d/usr_local_lib.conf #如果没有这个文件,自己建一个
/sbin/ldconfig

3.Redis 安装:
tar zxvf redis-3.0.0.tar.gz
cd redis-3.0.0
mkdir -p /usr/local/redis-3.0
# make prefix= install
# 将redis安装在/bin下/usr/local/redis-3.0/bin下
# make PREFIX=/usr/local/redis-3.0 install # 此方式安装,使用linux默认的内存分配方式
make PREFIX=/usr/local/redis-3.0 USE_TCMALLOC=yes FORCE_LIBC_MALLOC=yes install

4.建相关目录,并配置redis.conf 文件[简单配置]
cd /usr/local/redis-3.0/
mkdir etc #存放配置文件,这里目录名,可建为:conf
mkdir log #存放日志和pid文件
mkdir data #存放数据
vim etc/redis.conf
daemonize yes
port 6379
pidfile /usr/local/redis-3.0/log/redis_6379.pid
dir /usr/local/redis-3.0/data
logfile /usr/local/redis-3.0/log/redis_6379.log

5.Redis服务启动、关闭
(1).启动服务:
/usr/local/redis-3.0/bin/redis-server /usr/local/redis-3.0/etc/redis.conf
注:
此命令仅有一个启动参数,指定目录下的配置文件,不加参数执行默认配置。
测试启动:
/usr/local/redis-3.0/bin/redis-cli ping
返回PONG,则启动成功。
查看端口是否被占用:
netstat -ntlp |grep 6379

(2).关闭服务:
/usr/local/redis-3.0/bin/redis-cli shutdown
如果非默认端口,可指定端口:
/usr/local/redis-3.0/bin/redis-cli -p 6380 shutdown

(3).验证redis内存分配方式是否在使用tcmalloc
lsof -n |grep tcmalloc
redis-ser 2754 root mem REG 253,0 1078467 1725996 /usr/local/lib/libtcmalloc_minimal.so.4.1.2

6.Redis 服务启动脚本
redis 本身提供了redis安装脚本:utils/install_server.sh
但 这个脚本包括安装服务,有点烦,我们直接取其安装脚本:
#!/bin/sh
#Configurations injected by install_server below….
EXEC=/usr/local/redis-3.0/bin/redis-server
CLIEXEC=/usr/local/redis-3.0/bin/redis-cli
PIDFILE=/usr/local/redis-3.0/log/redis_6379.pid
CONF=”/usr/local/redis-3.0/etc/redis.conf”
REDISPORT=”6379″

case “$1” in
start)
if [ -f $PIDFILE ]
then
echo “$PIDFILE exists, process is already running or crashed”
else
echo “Starting Redis server…”
$EXEC $CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo “$PIDFILE does not exist, process is not running”
else
PID=$(cat $PIDFILE)
echo “Stopping …”
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo “Waiting for Redis to shutdown …”
sleep 1
done
echo “Redis stopped”
fi
;;
status)
PID=$(cat $PIDFILE)
if [ ! -x /proc/${PID} ]
then
echo ‘Redis is not running’
else
echo “Redis is running ($PID)”
fi
;;
restart)
$0 stop
$0 start
;;
*)
echo “Please use start, stop, restart or status as first argument”
;;
esac

Redis的复制流程

Redis的复制功能是基于内存快照的持久化策略基础上的,也就是说无论你的持久化策略选择的是什么,只要用到了Redis的复制功能,就一定会有内存快照发生,那么首先要注意你的系统内存容量规划

Redis复制流程在Slave和Master端各自是一套状态机流转,涉及的状态信息如下:
Slave 端:
REDIS_REPL_NONE
REDIS_REPL_CONNECT
REDIS_REPL_CONNECTED
Master端:

          REDIS_REPL_WAIT_BGSAVE_START
REDIS_REPL_WAIT_BGSAVE_END
REDIS_REPL_SEND_BULK
REDIS_REPL_ONLINE

整个状态机流程过程如下:
(1).Slave端在配置文件中添加了slave of指令,于是Slave启动时读取配置文件,初始状态为REDIS_REPL_CONNECT。
(2).Slave端在定时任务serverCron(Redis内部的定时器触发事件)中连接Master,发送sync命令,然后阻塞等待master发送回其内存快照文件(最新版的Redis已经不需要让Slave阻塞)。
     (3).Master端收到sync命令简单判断是否有正在进行的内存快照子进程,没有则立即开始内存快照,有则等待其结束,当快照完成后会将该文件发送给Slave端。
     (4).Slave端接收Master发来的内存快照文件,保存到本地,待接收完成后,清空内存表,重新读取Master发来的内存快照文件,重建整个内存表数据结构,并最终状态置位为 REDIS_REPL_CONNECTED状态,Slave状态机流转完成。
     (5).Master端在发送快照文件过程中,接收的任何会改变数据集的命令都会暂时先保存在Slave网络连接的发送缓存队列里(list数据结构),待快照完成后,依次发给Slave,之后收到的命令相同处理,并将状态置位为 REDIS_REPL_ONLINE。
整个复制过程完成,流程如下图所示:
 image1
Redis复制机制的缺陷

      从上面的流程可以看出,Slave从库在连接Master主库时,Master会进行内存快照,然后把整个快照文件发给Slave,
     也就是没有象MySQL那样有复制位置的概念,即无增量复制,这会给整个集群搭建带来非常多的问题。

比如一台线上正在运行的Master主库配置了一台从库进行简单读写分离,这时Slave由于网络或者其它原因与Master断开了连接,

     那么当Slave进行重新连接时,需要重新获取整个Master的内存快照,Slave所有数据跟着全部清除,
     然后重新建立整个内存表,一方面Slave恢复的时间会非常慢,另一方面也会给主库带来压力。
所以基于上述原因,如果你的Redis集群需要主从复制,那么最好事先配置好所有的从库,避免中途再去增加从库。

Cache还是Storage
在我们分析过了Redis的复制与持久化功能后,我们不难得出一个结论,实际上Redis目前发布的版本还都是一个单机版的思路,主要的问题集中在,持久化方式不够成熟,复制机制存在比较大的缺陷,这时我们又开始重新思考Redis的定位:Cache还是Storage?
如果作为Cache的话,似乎除了有些非常特殊的业务场景,必须要使用Redis的某种数据结构之外,我们使用Memcached可能更合适,毕竟Memcached无论客户端包和服务器本身更久经考验。
如果是作为存储Storage的话,我们面临的最大的问题是无论是持久化还是复制都没有办法解决Redis单点问题,即一台Redis挂掉了,没有太好的办法能够快速的恢复,通常几十G的持久化数据,Redis重启加载需要几个小时的时间,而复制又有缺陷,如何解决呢?

Redis可扩展集群搭建
1. 主动复制避开Redis复制缺陷。
既然Redis的复制功能有缺陷,那么我们不妨放弃Redis本身提供的复制功能,我们可以采用主动复制的方式来搭建我们的集群环境。
所谓主动复制是指由业务端或者通过代理中间件对Redis存储的数据进行双写或多写,通过数据的多份存储来达到与复制相同的目的,
               主动复制不仅限于用在Redis集群上,目前很多公司采用主动复制的技术来解决MySQL主从之间复制的延迟问题,
               比如Twitter还专门开发了用于复制和分区的中间件gizzard(https://github.com/twitter/gizzard) 。

主动复制虽然解决了被动复制的延迟问题,但也带来了新的问题,就是数据的一致性问题,数据写2次或多次,如何保证多份数据的一致性呢?

               如果你的应用对数据一致性要求不高,允许最终一致性的话,那么通常简单的解决方案是可以通过时间戳或者vector clock等方式,
               让客户端同时取到多份数据并进行校验,如果你的应用对数据一致性要求非常高,那么就需要引入一些复杂的一致性算法比如Paxos来保证数据的一致性,
               但是写入性能也会相应下降很多。

通过主动复制,数据多份存储我们也就不再担心Redis单点故障的问题了,如果一组Redis集群挂掉,我们可以让业务快速切换到另一组Redis上,降低业务风险。

2. 通过presharding进行Redis在线扩容。
通过主动复制我们解决了Redis单点故障问题,那么还有一个重要的问题需要解决:容量规划与在线扩容问题。
我们前面分析过Redis的适用场景是全部数据存储在内存中,而内存容量有限,那么首先需要根据业务数据量进行初步的容量规划,

                比如你的业务数据需要100G存储空间,假设服务器内存是48G,我们大约需要3~4台服务器来存储。
                这个实际是对现有业务情况所做的一个容量规划,假如业务增长很快,很快就会发现当前的容量已经不够了,Redis里面存储的数据很快就会超过物理内存大小,
                那么如何进行Redis的在线扩容呢?

Redis的作者提出了一种叫做presharding的方案来解决动态扩容和数据分区的问题,

     实际就是在同一台机器上部署多个Redis实例的方式,当容量不够时将多个实例拆分到不同的机器上,这样实际就达到了扩容的效果。

拆分过程如下:
在新机器上启动好对应端口的Redis实例。
配置新端口为待迁移端口的从库。
待复制完成,与主库完成同步后,切换所有客户端配置到新的从库的端口。
配置从库为新的主库。
移除老的端口实例。
重复上述过程迁移好所有的端口到指定服务器上。
     以上拆分流程是Redis作者提出的一个平滑迁移的过程,不过该拆分方法还是很依赖Redis本身的复制功能的,
     如果主库快照数据文件过大,这个复制的过程也会很久,同时会给主库带来压力。所以做这个拆分的过程最好选择为业务访问低峰时段进行。

Redis复制的改进思路
我们线上的系统使用了我们自己改进版的Redis,主要解决了Redis没有增量复制的缺陷,能够完成类似Mysql Binlog那样可以通过从库请求日志位置进行增量复制。
我们的持久化方案是首先写Redis的AOF文件,并对这个AOF文件按文件大小进行自动分割滚动,同时关闭Redis的Rewrite命令,
     然后会在业务低峰时间进行内存快照存储,并把当前的AOF文件位置一起写入到快照文件中,这样我们可以使快照文件与AOF文件的位置保持一致性,
     这样我们得到了系统某一时刻的内存快照,并且同时也能知道这一时刻对应的AOF文件的位置,那么当从库发送同步命令时,
     我们首先会把快照文件发送给从库,然后从库会取出该快照文件中存储的AOF文件位置,并将该位置发给主库,主库会随后发送该位置之后的所有命令,
     以后的复制就都是这个位置之后的增量信息了。
 image2
Redis与MySQL的结合
目前大部分互联网公司使用MySQL作为数据的主要持久化存储,那么如何让Redis与MySQL很好的结合在一起呢?
     我们主要使用了一种基于MySQL作为主库,Redis作为高速数据查询从库的异构读写分离的方案。

为此我们专门开发了自己的MySQL复制工具,可以方便的实时同步MySQL中的数据到Redis上。

 image3
(MySQL-Redis 异构读写分离)

总结:

Redis的复制功能没有增量复制,每次重连都会把主库整个内存快照发给从库,所以需要避免向在线服务的压力较大的主库上增加从库。
Redis的复制由于会使用快照持久化方式,所以如果你的Redis持久化方式选择的是日志追加方式(aof),

     那么系统有可能在同一时刻既做aof日志文件的同步刷写磁盘,又做快照写磁盘操作,这个时候Redis的响应能力会受到影响。
     所以如果选用aof持久化,则加从库需要更加谨慎。
可以使用主动复制和presharding方法进行Redis集群搭建与在线扩容。