传统的关系数据库具有不错的性能及稳定性,同时,久经历史考验,许多优秀的数据库沉淀了下来,比如MySQL。然而随着数据体积的爆发性增长,数据类型的增多,许多传统关系数据库扩展难的特点也爆发了出来,NoSQL数据库也应运而生。然而区别于以往的使用方法,许多NoSQL都有着自己的限制,从而也导致了入门难的问题。这里我们为大家分享上海创行科技技术总监严澜的博文——如何搭建高效的MongoDB集群。

以下为原文

在大数据的时代,传统的关系型数据库要能更高的服务必须要解决高并发读写、海量数据高效存储、高可扩展性和高可用性这些难题。不过就是因为这些问题Nosql诞生了。

NOSQL有这些优势:

  • 大数据量,可以通过廉价服务器存储大量的数据,轻松摆脱传统mysql单表存储量级限制。
  • 高扩展性,Nosql去掉了关系数据库的关系型特性,很容易横向扩展,摆脱了以往老是纵向扩展的诟病。
  • 高性能,Nosql通过简单的key-value方式获取数据,非常快速。还有NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多。
  • 灵活的数据模型,NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。
  • 高可用,NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如mongodb通过mongos、mongo分片就可以快速配置出高可用配置。
  • 在nosql数据库里,大部分的查询都是键值对(key、value)的方式。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中最像关系数据库的。支持类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。所以这个非常方便,我们可以用sql操作MongoDB,从关系型数据库迁移过来,开发人员学习成本会大大减少。如果再对底层的sql API做一层封装,开发基本可以感觉不到mongodb和关系型数据库的区别。同样MongoDB也是号称自己能够快速搭建一个高可用可扩展的的分布式集群,网上有很多搭建的文章,在我们搭建的时候还需要查找修改很多东西,所以把自己实战的步骤记录下来以备忘。我们看看如何一步一步搭建这个东东。

一、mongodb单实例。这种配置只适合简易开发时使用,生产使用不行,因为单节点挂掉整个数据业务全挂,如下图。

虽然不能生产使用,但这个模式可以快速搭建启动,并且能够用mongodb的命令操作数据库。下面列出在linux下安装单节点mongodb的步骤

1. 建立mongodb测试文件夹

[js]  view plain copy
  1. #存放整个mongodb文件
  2. mkdir -p /data/mongodbtest/single
  3. #存放mongodb数据文件
  4. mkdir -p /data/mongodbtest/single/data
  5. #进入mongodb文件夹
  6. cd  /data/mongodbtest/single

2. 下载mongodb的安装程序包

[js]  view plain copy
  1. wget <a href="http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.6.tgz">http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.6.tgz</a>
  2. #解压下载的压缩包
  3. tar xvzf mongodb-linux-x86_64-2.4.6.tgz
  4. #进入mongodb程序执行文件夹
  5. cd mongodb-linux-x86_64-2.4.6/bin/

3. 启动单实例mongodb

[js]  view plain copy
  1. mongod  --dbpath /data/mongodbtest/single/data

输出日志如下,成功!

[initandlisten] db version v2.4.6 
…….. 
[initandlisten] waiting for connections on port 27017 
[websvr] admin web console waiting for connections on port 28017

mongodb默认自带提供了web访问接口,通过 IP + 端口的形式可以访问。

http://192.168.0.1:28017/

二、主从模式。使用mysql数据库时大家广泛用到,采用双机备份后主节点挂掉了后从节点可以接替主机继续服务。所以这种模式比单节点的高可用性要好很多。

下面看一下怎么一步步搭建一个mongodb的主从复制节点:

1. 准备两台机器 192.168.0.1 和 192.168.0.2。 192.168.0.1 当作主节点, 192.168.0.2作为从节点。

2. 分别下载mongodb安装程序包。在192.168.0.1上建立文件夹 /data/mongodbtest/master,192.168.0.2建立文件夹/data/mongodbtest/slave。

3. 在192.168.0.1启动mongodb主节点程序。注意后面的这个 “ –master ”参数,标示主节点。

mongod –dbpath /data/mongodbtest/master –master

输出日志如下,成功!

[initandlisten] MongoDB starting : pid=18285 port=27017 dbpath=/data/mongodbtest/master master=1

#日志显示主节点参数

[initandlisten] options: { dbpath: “/data/mongodbtest/master”, master: true } 
…….. 
[initandlisten] waiting for connections on port 27017

4. 在192.168.0.2启动mongodb从节点程序。关键配置,指定主节点ip地址和端口 –source 192.168.0.1:27017 和 标示从节点 –source 参数。

mongod –dbpath /data/mongodbtest/slave –slave –source 192.168.0.1:27017

输出日志如下,成功!

[initandlisten] MongoDB starting : pid=17888 port=27017 dbpath=/data/mongodbtest/slave slave=1
…….. 
#日志显示从节点参数 
[initandlisten] options: { dbpath: “/data/mongodbtest/slave”, slave: true, source: “192.168.0.1:27017″ } 
…….. 
[initandlisten] waiting for connections on port 27017 
#日志显示从节点 从主节点同步复制数据 
[replslave] repl: from host:192.168.0.1:27017

5. 测试主从复制。

在主节点上连接到终端:

js] view plaincopy

  1. mongo 127.0.0.1
  2. #建立test 数据库。
  3. use test;
  4. 往testdb表插入数据。
  5. > db.testdb.insert({"test1":"testval1"})
  6. 查询testdb数据看看是否成功。
  7. > db.testdb.find();
  8. { "_id" : ObjectId("5284e5cb1f4eb215b2ecc463"), "test1" : "testval1" }

可以看到主机的同步日志

[initandlisten] connection accepted from 192.168.0.2:37285 #3 (2 connections now open)

[slaveTracking] update local.slaves query: { _id: ObjectId(’5284e6268ed115d6238bdb39′), config: { host: “192.168.0.2:35271″, upgradeNeeded: true }, ns: “local.oplog.$main” } update: { $set: { syncedTo: Timestamp 1384441570000|1 } } nscanned:1 nupdated:1 fastmod:1 keyUpdates:0 locks(micros) w:132015 132ms

检查从主机的数据。

mongo 127.0.0.1

查看当前数据库。

[js] view plaincopy

  1. > show dbs;
  2. local   0.203125GB
  3. test    0.203125GB
  4. use test;
  5. db.testdb.find();
  6. { "_id" : ObjectId("5284e5cb1f4eb215b2ecc463"), "test1" : "testval1" }

查询后数据已经同步过来了。再看看日志,发现从主机确实从主机同步了数据。

js]  view plain copy
  1. Thu Nov 14 23:05:13 [replslave] repl:   checkpoint applied 15 operations
  2. Thu Nov 14 23:05:13 [replslave] repl:   syncedTo: Nov 14 23:08:10 5284e75a:1

查看服务状态

  1. > db.printReplicationInfo();
  2. this is a slave, printing slave replication info.
  3. source:   192.168.0.1:27017
  4. syncedTo: Sun Nov 17 2013 16:04:02 GMT+0800 (CST)
  5. = -54 secs ago (-0.01hrs)

到此主从结构的mongodb搭建好了。 
故障转移测试
,现在两台服务器如果主服务器挂掉了,从服务器可以正常运转吗?

a. 先测试下从服务器可以当成主服务器吗,也就是往从服务器里写能够同步主服务器吗?在192.168.0.2上连接mongodb。

[js]  view plain copy
  1. mongo 127.0.0.1:27017
  2. > db.testdb.insert({"test3":"testval3"});
  3. not master

可以看到 mongodb的从节点是不能提供写操作的,只能提供读操作。

b. 如果从服务器挂掉,主服务器还可以提供服务。如果主服务器挂掉了从服务器能否自动变为可写。
测试一下!

先杀掉原来的mongodb主服务器。

[js]  view plain copy
  1. kill -3 `ps -ef|grep mongod|grep -v grep|awk '{print $2}'`

测试从服务器能否可写。在192.168.0.2上连接mongodb测试。

[js]  view plain copy
  1. > db.testdb.insert({"test3":"testval3"});
  2. not master

看起来从服务器没有自动接替主服务器的功能,只有手工处理了!

停止从服务器,在原数据文件启动并添加主服务器标示。《?是这样吗?求证。。。》

[js]  view plain copy
  1. mongod  --dbpath /data/mongodbtest/slave --master

等到启动成功(时间有点长)。在192.168.0.2 上 连接

[js]  view plain copy
  1. mongo 192.168.0.2:27017
[js]  view plain copy
  1. > db.testdb.find();
  2. { "_id" : ObjectId("5288629e9b0318be4b20bd4c"), "test1" : "testval1" }
  3. { "_id" : ObjectId("528862d69b0318be4b20bd4d"), "test2" : "testval2" }

成功!

多个从节点。现在只是一个数据库服务器又提供写又提供读,机器承载会出现瓶颈。大家还记得mysql里的读写分离吗?把20%的写放到主节点,80%的读放到从节点分摊了减少了服务器的负载。但是大部分应用都是读操作带来的压力,一个从节点压力负载不了,可以把一个从节点变成多个节点。那mongodb的一主多从可以支持吗?答案是肯定的。

为了方便测试,在192.168.0.2上再建立一个文件夹 /data/mongodbtest/slave1 作为另一个slave服务器。启动slave2服务,

[js]  view plain copy
  1. mongod  --dbpath /data/mongodbtest/slave1 --slave  --port 27017 --source 192.168.0.1:27017。

成功启动后通过mongodb连接测试:

[js]  view plain copy
  1. > db.testdb.find();
  2. { "_id" : ObjectId("5288629e9b0318be4b20bd4c"), "test1" : "testval1" }
  3. { "_id" : ObjectId("528862d69b0318be4b20bd4d"), "test2" : "testval2" }

搭建了这套主从复制系统是不是就很稳健了,其实不然。。。看看这几个问题?

  • 主节点挂了能否自动切换连接?目前需要手工切换。
  • 主节点的写压力过大如何解决?
  • 从节点每个上面的数据都是对数据库全量拷贝,从节点压力会不会过大?
  • 就算对从节点路由实施路由访问策略能否做到自动扩展?
  • 这篇文章看完这些问题就可以搞定了。NoSQL的产生就是为了解决大数据量、高扩展性、高性能、灵活数据模型、高可用性。但是光通过主从模式的架构远远达不到上面几点,由此MongoDB设计了副本集和分片的功能。这篇文章主要介绍副本集

    mongoDB官方已经不建议使用主从模式了,替代方案是采用副本集的模式, 点击查看,如图: 

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

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

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

  • 副本集的配置操作步骤:
  • 1、准备两台机器 192.168.1.136、192.168.1.137、192.168.1.138。 192.168.1.136 当作副本集主节点,192.168.1.137、192.168.1.138作为副本集副本节点

    2、分别在每台机器上建立mongodb副本集测试文件夹

    #存放整个mongodb文件
    mkdir -p /data/mongodbtest/replset #存放mongodb数据文件
    mkdir -p /data/mongodbtest/replset/data#进入mongodb文件夹
    cd  /data/mongodbtest
    

    3、下载mongodb的安装程序包

    wget <a href="http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.8.tgz">http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.4.8.tgz</a>
    

    注意linux生产环境不能安装32位的mongodb,因为32位受限于操作系统最大2G的文件限制。

    #解压下载的压缩包
    tar xvzf mongodb-linux-x86_64-2.4.8.tgz
    

    4、分别在每台机器上启动mongodb

    /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongod  --dbpath /data/mongodbtest/replset/data   --replSet repset
    

    可以看到控制台上显示副本集还没有配置初始化信息。

    [plain]  view plain copy
    1. Sun Dec 29 20:12:02.953 [rsStart] replSet can't get local.system.replset config from self or any seed (EMPTYCONFIG)
    2. Sun Dec 29 20:12:02.953 [rsStart] replSet info you may need to run  replSetInitiate -- rs.initiate() in the shell -- if that is not already done

    5、初始化副本集

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

    /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo#使用admin数据库
    use admin
    

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

    config = { _id:"repset", members:[
    ... {_id:0,host:"192.168.1.136:27017"},
    ... {_id:1,host:"192.168.1.137:27017"},
    ... {_id:2,host:"192.168.1.138:27017"}]
    ... }
    

    #输出

    [plain]  view plain copy
    1. {
    2. "_id" : "repset",
    3. "members" : [
    4. {
    5. "_id" : 0,
    6. "host" : "192.168.1.136:27017"
    7. },
    8. {
    9. "_id" : 1,
    10. "host" : "192.168.1.137:27017"
    11. },
    12. {
    13. "_id" : 2,
    14. "host" : "192.168.1.138:27017"
    15. }
    16. ]
    17. }
    #初始化副本集配置
    rs.initiate(config);
    

    #输出成功

    [plain]  view plain copy
    1. {
    2. "info" : "Config now saved locally.  Should come online in about a minute.",
    3. "ok" : 1
    4. }

    #查看日志,副本集启动成功后,138为主节点PRIMARY,136、137为副本节点SECONDARY

    [plain]  view plain copy
    1. Sun Dec 29 20:26:13.842 [conn3] replSet replSetInitiate admin command
    2. received from client
    3. Sun Dec 29 20:26:13.842 [conn3] replSet replSetInitiate config object parses ok, 3 members specified Sun Dec 29 20:26:13.847 [conn3]
    4. replSet replSetInitiate all members seem up Sun Dec 29 20:26:13.848 [conn3]
    5. ****** Sun Dec 29 20:26:13.848 [conn3] creating replication oplog of size:
    6. 990MB... Sun Dec 29 20:26:13.849 [FileAllocator] allocating new datafile
    7. /data/mongodbtest/replset/data/local.1, filling with zeroes... Sun Dec
    8. 29 20:26:13.862 [FileAllocator] done allocating datafile /data/mongodbtest/replset/data/local.1,
    9. size: 1024MB, took 0.012 secs Sun Dec 29 20:26:13.863 [conn3] ****** Sun
    10. Dec 29 20:26:13.863 [conn3] replSet info saving a newer config version
    11. to local.system.replset Sun Dec 29 20:26:13.864 [conn3] replSet saveConfigLocally
    12. done Sun Dec 29 20:26:13.864 [conn3] replSet replSetInitiate config now
    13. saved locally. Should come online in about a minute. Sun Dec 29 20:26:23.047
    14. [rsStart] replSet I am 192.168.1.138:27017 Sun Dec 29 20:26:23.048 [rsStart]
    15. replSet STARTUP2 Sun Dec 29 20:26:23.049 [rsHealthPoll] replSet member
    16. 192.168.1.137:27017 is up Sun Dec 29 20:26:23.049 [rsHealthPoll] replSet
    17. member 192.168.1.136:27017 is up Sun Dec 29 20:26:24.051 [rsSync] replSet
    18. SECONDARY Sun Dec 29 20:26:25.053 [rsHealthPoll] replset info 192.168.1.136:27017
    19. thinks that we are down Sun Dec 29 20:26:25.053 [rsHealthPoll] replSet
    20. member 192.168.1.136:27017 is now in state STARTUP2 Sun Dec 29 20:26:25.056
    21. [rsMgr] not electing self, 192.168.1.136:27017 would veto with 'I don't
    22. think 192.168.1.138:27017 is electable' Sun Dec 29 20:26:31.059 [rsHealthPoll]
    23. replset info 192.168.1.137:27017 thinks that we are down Sun Dec 29 20:26:31.059
    24. [rsHealthPoll] replSet member 192.168.1.137:27017 is now in state STARTUP2
    25. Sun Dec 29 20:26:31.062 [rsMgr] not electing self, 192.168.1.137:27017
    26. would veto with 'I don't think 192.168.1.138:27017 is electable' Sun Dec
    27. 29 20:26:37.074 [rsMgr] replSet info electSelf 2 Sun Dec 29 20:26:38.062
    28. [rsMgr] replSet PRIMARY Sun Dec 29 20:26:39.071 [rsHealthPoll] replSet
    29. member 192.168.1.137:27017 is now in state RECOVERING Sun Dec 29 20:26:39.075
    30. [rsHealthPoll] replSet member 192.168.1.136:27017 is now in state RECOVERING
    31. Sun Dec 29 20:26:42.201 [slaveTracking] build index local.slaves { _id:
    32. 1 } Sun Dec 29 20:26:42.207 [slaveTracking] build index done. scanned 0
    33. total records. 0.005 secs
    34. Sun Dec 29 20:26:43.079 [rsHealthPoll] replSet
    35. member 192.168.1.136:27017 is now in state SECONDARY
    36. Sun Dec 29 20:26:49.080
    37. [rsHealthPoll] replSet member 192.168.1.137:27017 is now in state SECONDARY
    #查看集群节点的状态 rs.status();

    #输出

    [plain]  view plain copy
    1. {
    2. "set" : "repset",
    3. "date" : ISODate("2013-12-29T12:54:25Z"),
    4. "myState" : 1,
    5. "members" : [
    6. {
    7. "_id" : 0,
    8. "name" : "192.168.1.136:27017",
    9. "health" : 1,
    10. "state" : 2,
    11. "stateStr" : "SECONDARY",
    12. "uptime" : 1682,
    13. "optime" : Timestamp(1388319973, 1),
    14. "optimeDate" : ISODate("2013-12-29T12:26:13Z"),
    15. "lastHeartbeat" : ISODate("2013-12-29T12:54:25Z"),
    16. "lastHeartbeatRecv" : ISODate("2013-12-29T12:54:24Z"),
    17. "pingMs" : 1,
    18. "syncingTo" : "192.168.1.138:27017"
    19. },
    20. {
    21. "_id" : 1,
    22. "name" : "192.168.1.137:27017",
    23. "health" : 1,
    24. "state" : 2,
    25. "stateStr" : "SECONDARY",
    26. "uptime" : 1682,
    27. "optime" : Timestamp(1388319973, 1),
    28. "optimeDate" : ISODate("2013-12-29T12:26:13Z"),
    29. "lastHeartbeat" : ISODate("2013-12-29T12:54:25Z"),
    30. "lastHeartbeatRecv" : ISODate("2013-12-29T12:54:24Z"),
    31. "pingMs" : 1,
    32. "syncingTo" : "192.168.1.138:27017"
    33. },
    34. {
    35. "_id" : 2,
    36. "name" : "192.168.1.138:27017",
    37. "health" : 1,
    38. "state" : 1,
    39. "stateStr" : "PRIMARY",
    40. "uptime" : 2543,
    41. "optime" : Timestamp(1388319973, 1),
    42. "optimeDate" : ISODate("2013-12-29T12:26:13Z"),
    43. "self" : true
    44. }
    45. ],
    46. "ok" : 1
    47. }

    整个副本集已经搭建成功了。

    6、测试副本集数据复制功能

    #在主节点192.168.1.138 上连接到终端:
    mongo 127.0.0.1#建立test 数据库。
    use test;往testdb表插入数据。
    > db.testdb.insert({"test1":"testval1"})#在副本节点 192.168.1.136、192.168.1.137 上连接到mongodb查看数据是否复制过来。
    /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 192.168.1.136:27017#使用test 数据库。
    repset:SECONDARY> use test;repset:SECONDARY> show tables;
    

    #输出

    [plain]  view plain copy
    1. Sun Dec 29 21:50:48.590 error: { "$err" : "not master and slaveOk=false", "code" : 13435 } at src/mongo/shell/query.js:128
    #mongodb默认是从主节点读写数据的,副本节点上不允许读,需要设置副本节点可以读。
    repset:SECONDARY> db.getMongo().setSlaveOk();#可以看到数据已经复制到了副本集。
    repset:SECONDARY> db.testdb.find();
    
    [plain]  view plain copy
    1. #输出
    2. { "_id" : ObjectId("52c028460c7505626a93944f"), "test1" : "testval1" }

    7、测试副本集故障转移功能

    先停掉主节点mongodb 138,查看136、137的日志可以看到经过一系列的投票选择操作,137 当选主节点,136从137同步数据过来。

    [plain]  view plain copy
    1. Sun Dec 29 22:03:05.351 [rsBackgroundSync] replSet sync source problem:
    2. 10278 dbclient error communicating with server: 192.168.1.138:27017
    3. Sun Dec 29 22:03:05.354 [rsBackgroundSync] replSet syncing to: 192.168.1.138:27017
    4. Sun Dec 29 22:03:05.356 [rsBackgroundSync] repl: couldn't connect to server
    5. 192.168.1.138:27017
    6. Sun Dec 29 22:03:05.356 [rsBackgroundSync] replSet
    7. not trying to sync from 192.168.1.138:27017, it is vetoed for 10 more seconds
    8. Sun Dec 29 22:03:05.499 [rsHealthPoll] DBClientCursor::init call() failed
    9. Sun Dec 29 22:03:05.499 [rsHealthPoll] replset info 192.168.1.138:27017
    10. heartbeat failed, retrying Sun Dec 29 22:03:05.501 [rsHealthPoll] replSet
    11. info 192.168.1.138:27017 is down (or slow to respond): Sun Dec 29 22:03:05.501
    12. [rsHealthPoll] replSet member 192.168.1.138:27017 is now in state DOWN
    13. Sun Dec 29 22:03:05.511 [rsMgr] not electing self, 192.168.1.137:27017
    14. would veto with '192.168.1.136:27017 is trying to elect itself but 192.168.1.138:27017
    15. is already primary and more up-to-date' Sun Dec 29 22:03:07.330 [conn393]
    16. replSet info voting yea for 192.168.1.137:27017 (1) Sun Dec 29 22:03:07.503
    17. [rsHealthPoll] replset info 192.168.1.138:27017 heartbeat failed, retrying
    18. Sun Dec 29 22:03:08.462 [rsHealthPoll] replSet member 192.168.1.137:27017
    19. is now in state PRIMARY Sun Dec 29 22:03:09.359 [rsBackgroundSync] replSet
    20. syncing to: 192.168.1.137:27017 Sun Dec 29 22:03:09.507 [rsHealthPoll]
    21. replset info 192.168.1.138:27017 heartbeat failed, retrying

    查看整个集群的状态,可以看到138为状态不可达。

    /data/mongodbtest/mongodb-linux-x86_64-2.4.8/bin/mongo 192.168.1.136:27017repset:SECONDARY> rs.status();
    

    #输出

    [plain]  view plain copy
    1. {
    2. "set" : "repset",
    3. "date" : ISODate("2013-12-29T14:28:35Z"),
    4. "myState" : 2,
    5. "syncingTo" : "192.168.1.137:27017",
    6. "members" : [
    7. {
    8. "_id" : 0,
    9. "name" : "192.168.1.136:27017",
    10. "health" : 1,
    11. "state" : 2,
    12. "stateStr" : "SECONDARY",
    13. "uptime" : 9072,
    14. "optime" : Timestamp(1388324934, 1),
    15. "optimeDate" : ISODate("2013-12-29T13:48:54Z"),
    16. "self" : true
    17. },
    18. {
    19. "_id" : 1,
    20. "name" : "192.168.1.137:27017",
    21. "health" : 1,
    22. "state" : 1,
    23. "stateStr" : "PRIMARY",
    24. "uptime" : 7329,
    25. "optime" : Timestamp(1388324934, 1),
    26. "optimeDate" : ISODate("2013-12-29T13:48:54Z"),
    27. "lastHeartbeat" : ISODate("2013-12-29T14:28:34Z"),
    28. "lastHeartbeatRecv" : ISODate("2013-12-29T14:28:34Z"),
    29. "pingMs" : 1,
    30. "syncingTo" : "192.168.1.138:27017"
    31. },
    32. {
    33. "_id" : 2,
    34. "name" : "192.168.1.138:27017",
    35. "health" : 0,
    36. "state" : 8,
    37. "stateStr" : "(not reachable/healthy)",
    38. "uptime" : 0,
    39. "optime" : Timestamp(1388324934, 1),
    40. "optimeDate" : ISODate("2013-12-29T13:48:54Z"),
    41. "lastHeartbeat" : ISODate("2013-12-29T14:28:35Z"),
    42. "lastHeartbeatRecv" : ISODate("2013-12-29T14:28:23Z"),
    43. "pingMs" : 0,
    44. "syncingTo" : "192.168.1.137:27017"
    45. }
    46. ],
    47. "ok" : 1
    48. }

    再启动原来的主节点 138,发现138 变为 SECONDARY,还是137 为主节点 PRIMARY。

    [plain]  view plain copy
    1. Sun Dec 29 22:21:06.619 [rsStart] replSet I am 192.168.1.138:27017
    2. Sun Dec 29 22:21:06.619 [rsStart] replSet STARTUP2
    3. Sun Dec 29 22:21:06.627 [rsHealthPoll] replset info 192.168.1.136:27017 thinks that we are down
    4. Sun Dec 29 22:21:06.627 [rsHealthPoll] replSet member 192.168.1.136:27017 is up
    5. Sun Dec 29 22:21:06.627 [rsHealthPoll] replSet member 192.168.1.136:27017 is now in state SECONDARY
    6. Sun Dec 29 22:21:07.628 [rsSync] replSet SECONDARY
    7. Sun Dec 29 22:21:08.623 [rsHealthPoll] replSet member 192.168.1.137:27017 is up
    8. Sun Dec 29 22:21:08.624 [rsHealthPoll] replSet member 192.168.1.137:27017 is now in state PRIMARY

    8、java程序连接副本集测试。三个节点有一个节点挂掉也不会影响应用程序客户端对整个副本集的读写!

    [java]  view plain copy
    1. public class TestMongoDBReplSet { public static void main(String[] args)
    2. { try { List<ServerAddress> addresses = new ArrayList<ServerAddress>();
    3. ServerAddress address1 = new ServerAddress("192.168.1.136" , 27017);
    4. ServerAddress  address2 = new ServerAddress("192.168.1.137" , 27017);
    5. ServerAddress address3 = new ServerAddress("192.168.1.138" , 27017);
    6. addresses.add(address1);
    7. addresses.add(address2);
    8. addresses.add(address3);
    9. MongoClient client =  new MongoClient(addresses);
    10. DB db = client.getDB( "test");
    11. DBCollection  coll = db.getCollection( "testdb");
    12. // 插入
    13. BasicDBObject object = new BasicDBObject();
    14. object.append( "test2", "testval2" );
    15. coll.insert(object);   DBCursor dbCursor = coll.find();
    16. while (dbCursor.hasNext())
    17. { DBObject dbObject = dbCursor.next();                          System. out.println(dbObject.toString());
    18. } } catch (Exception e) {
    19. e.printStackTrace();
    20. } } }

    目前看起来支持完美的故障转移了,这个架构是不是比较完美了?其实还有很多地方可以优化,比如开头的第二个问题:主节点的读写压力过大如何解决?常见的解决方案是读写分离,mongodb副本集的读写分离如何做呢?

    看图说话:

    常规写操作来说并没有读操作多,所以一台主节点负责写,两台副本节点负责读。

    1、设置读写分离需要先在副本节点SECONDARY 设置 setSlaveOk。 
    2、在程序中设置副本节点负责读操作,如下代码:

    [java]  view plain copy
    1. public class TestMongoDBReplSetReadSplit {
    2. public static void main(String[] args) {
    3. try {
    4. List<ServerAddress> addresses = new ArrayList<ServerAddress>();
    5. ServerAddress address1 = new ServerAddress("192.168.1.136" , 27017);
    6. ServerAddress address2 = new ServerAddress("192.168.1.137" , 27017);
    7. ServerAddress address3 = new ServerAddress("192.168.1.138" , 27017);
    8. addresses.add(address1);
    9. addresses.add(address2);
    10. addresses.add(address3);
    11. MongoClient client = new MongoClient(addresses);
    12. DB db = client.getDB( "test" );
    13. DBCollection coll = db.getCollection( "testdb" );
    14. BasicDBObject object = new BasicDBObject();
    15. object.append( "test2" , "testval2" );
    16. //读操作从副本节点读取
    17. ReadPreference preference = ReadPreference. secondary();
    18. DBObject dbObject = coll.findOne(object, null , preference);
    19. System. out .println(dbObject);
    20. } catch (Exception e) {
    21. e.printStackTrace();
    22. }
    23. }
    24. }

    读参数除了secondary一共还有五个参数:primary、primaryPreferred、secondary、secondaryPreferred、nearest。

    primary:默认参数,只从主节点上进行读取操作; 
    primaryPreferred:大部分从主节点上读取数据,只有主节点不可用时从secondary节点读取数据。 
    secondary:只从secondary节点上进行读取操作,存在的问题是secondary节点的数据会比primary节点数据“旧”。 
    secondaryPreferred:优先从secondary节点进行读取操作,secondary节点不可用时从主节点读取数据; 
    nearest:不管是主节点、secondary节点,从网络延迟最低的节点上读取数据。

    好,读写分离做好我们可以数据分流,减轻压力解决了“主节点的读写压力过大如何解决?”这个问题。不过当我们的副本节点增多时,主节点的复制压力会加大有什么办法解决吗?mongodb早就有了相应的解决方案。

    看图: 

    其中的仲裁节点不存储数据,只是负责故障转移的群体投票,这样就少了数据复制的压力《?》。是不是想得很周到啊,一看mongodb的开发兄弟熟知大数据架构体系,其实不只是主节点、副本节点、仲裁节点,还有Secondary-Only、Hidden、Delayed、Non-Voting。

    Secondary-Only:不能成为primary节点,只能作为secondary副本节点,防止一些性能不高的节点成为主节点。 
    Hidden:这类节点是不能够被客户端制定IP引用,也不能被设置为主节点,但是可以投票,一般用于备份数据。 
    Delayed:可以指定一个时间延迟从primary节点同步数据。主要用于备份数据,如果实时同步,误删除数据马上同步到从节点,恢复又恢复不了。 
    Non-Voting:没有选举权的secondary节点,纯粹的备份数据节点。

    到此整个mongodb副本集搞定了两个问题:

    • 主节点挂了能否自动切换连接?目前需要手工切换。
    • 主节点的读写压力过大如何解决?

    还有这两个问题后续解决:

    • 从节点每个上面的数据都是对数据库全量拷贝,从节点压力会不会过大?
    • 数据压力大到机器支撑不了的时候能否做到自动扩展?

    做了副本集发现又一些问题:

    • 副本集故障转移,主节点是如何选举的?能否手动干涉下架某一台主节点。
    • 官方说副本集数量最好是奇数,为什么?
    • mongodb副本集是如何同步的?如果同步不及时会出现什么情况?会不会出现不一致性?
    • mongodb的故障转移会不会无故自动发生?什么条件会触发?频繁触发可能会带来系统负载加重

    参考:

    http://cn.docs.mongodb.org/manual/administration/replica-set-member-configuration/

    http://docs.mongodb.org/manual/reference/connection-string/

    http://www.cnblogs.com/magialmoon/p/3268963.html

    原文链接:

    搭建高可用mongodb集群(一)——配置mongodb

    搭建高可用mongodb集群(二)—— 副本集  (责编/仲浩)

搭建高可用的MongoDB集群(一):MongoDB的配置与副本集相关推荐

  1. 搭建高可用mongodb集群(二)—— 副本集

    2019独角兽企业重金招聘Python工程师标准>>> 在上一篇文章<搭建高可用MongoDB集群(一)--配置MongoDB> 提到了几个问题还没有解决. 主节点挂了能 ...

  2. 16、Kubernetes搭建高可用集群

    文章目录 前言 一.高可用集群 1.1 高可用集群技术细节 二.部署高可用集群 2.1 准备环境 2.2 所有master节点部署keepalived 2.2.1 安装相关包和keepalived 2 ...

  3. 二进制搭建kubernetes多master集群【三、配置k8s master及高可用】

    前面两篇文章已经配置好了etcd和flannel的网络,现在开始配置k8s master集群. etcd集群配置参考:二进制搭建kubernetes多master集群[一.使用TLS证书搭建etcd集 ...

  4. MyCAT+MySQL 搭建高可用企业级数据库集群

    第1章 课程介绍 课程介绍 1-1 MyCAT导学 试看 1-2 课程介绍 第2章 MyCAT入门 这一章中,我们将回顾了垂直切分,水平切分,分库分表等基础概念,然后快速回如何安装和启动MyCAT的, ...

  5. 用Keepalived搭建高可用集群

    一.集群介绍 1.根据功能划分为两大类:高可用和负载均衡 2.高可用:一台工作,另一台冗余,当一台服务器故障宕机时,另一台服务器提供服务.开源软件有heartbeat,keepalived. 3.负载 ...

  6. 【Nginx那些事】nginx配置实例(四)搭建高可用集群

    [Nginx那些事]nginx配置实例(四)搭建高可用集群 nginx 实现高可用 安装keepalived keepalived配置 主Nginx服务器配置 从nginx服务器配置 脚本文件权限设置 ...

  7. k8s高可用集群_搭建高可用集群(初始化和部署keepalived)---K8S_Google工作笔记0055

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 然后我们来部署keepalived,搭建高可用集群. 然后我们这里需要master,155, ma ...

  8. k8s高可用集群_搭建高可用集群(实现方式介绍)---K8S_Google工作笔记0054

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 然后我们来说搭建高可用集群,为什么要搭建高可用集群. 因为,首先我们说master节点是用来管理其 ...

  9. 故障转移集群搭建高可用文件共享服务器

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.故障转移集群是什么? 二.什么是仲裁盘? 三.添加故障转移集群功能 四.故障转移集群的建立 五.配置集群仲裁盘 六 ...

最新文章

  1. redis五大数据类型使用场景
  2. 数值和字符串互相转换
  3. html如何获得网址,已知一个网址,如何获得该网址对应的html源代码?在del 找人事管理软件...
  4. Hadoop Yarn生产环境核心配置参数
  5. python中的文件I/O
  6. 工商银行:应用多k8s集群管理及容灾实践
  7. 记录java对象修改过的字段_Java垃圾回收器与内存回收策略
  8. web api 开发之 filter
  9. JAVA开发一个合并单元格报表_9、docx4j实现动态表格(编程式)单元格合并
  10. 第 5-8 课:综合实战客户管理系统(⼆)
  11. html简单随机抽奖页面(在线抽奖、随机选取、自动挑选)
  12. 类和对象的关系练习题:需求:将汽车改装成3个轮子的车并换个颜色。
  13. word如何用制表符对齐公式
  14. 360度反馈调查表中的问题示范
  15. 使用HEXO搭建个人博客时遇到的问题日志 PART.1
  16. vip163邮箱手机版登陆入口是哪个?vip邮箱163实用技巧
  17. APS系统六大要素是什么?企业导入APS(高级生产计划与排程)有哪些好处?
  18. 總結——關於2017 10 30測試的分析總結
  19. Window下python安装metis
  20. ChatGPT专业应用:生成行业分析报告

热门文章

  1. android read phone state,[应用宝] 渠道审核之 READ_PHONE_STATE 权限特殊处理
  2. 淘宝店铺引流是什么意思?
  3. 【收藏】简单方便的自助106短信群发工具
  4. python 控制台输出彩色文字及背景
  5. POJO,PO,VO,DTO
  6. 索尼爱立信哪款手机java最强,急!!!手机索尼爱立信S500C与三星SGH-E958哪款好?...
  7. c++环境下qt+pcpp(winpcap)实现的网络抓包(sniff)程序
  8. 推荐系统实践(一)----基于用户的协同过滤算法(UserCF)
  9. oracle trim函数的用法
  10. 锁屏后后台服务停止运行的解决方案