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

在上一篇文章《MongoDB 3.4 高可用集群搭建(一):主从模式》提到了几个问题还没有解决。

  • 主节点挂了能否自动切换连接?目前需要手工切换。
  • 主节点的读写压力过大如何解决?
  • 从节点每个上面的数据都是对数据库全量拷贝,从节点压力会不会过大?
  • 数据压力大到机器支撑不了的时候能否做到自动扩展?

这篇文章看完这些问题就可以搞定了。NoSQL的产生就是为了解决大数据量、高扩展性、高性能、灵活数据模型、高可用性。但是光通过主从模式的架构远远达不到上面几点,由此MongoDB设计了副本集和分片的功能。这篇文章主要介绍副本集

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

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

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

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

1、准备两台机器 10.202.11.117,10.202.11.118,10.202.37.75。 10.202.11.117 当作副本集主节点,10.202.11.118,10.202.37.75作为副本集副本节点

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

在mongodb-3.4.2目录下创建replset/data

3、下载mongodb的安装程序包

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

  1. ./mongod --dbpath=/home/appdeploy/dev/mongodb/mongodb-3.4./replset/data --port= --fork --logpath=/home/appdeploy/dev/mongodb/mongodb-3.4./logs/mongodb.log --httpinterface --rest --replSet repset
  1. 主要是参数:--replSet repset

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

  1. --29T10::59.878+ I REPL [initandlisten] Did not find local voted for document at startup.
  2. --29T10::59.878+ I REPL [initandlisten] Did not find local replica set configuration document at startup; NoMatchingDocument: Did not find replica set configuration document in local.system.replset
 

5、初始化副本集

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

  1. #使用admin数据库
  2. use admin

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

  1. > config = { _id:"repset", members:[
  2. ... {_id:,host:"10.202.11.117:27017"},
  3. ... {_id:,host:"10.202.11.118:27017"},
  4. ... {_id:,host:"10.202.37.75:27017"}]
  5. ... }
  6. {
  7. "_id" : "repset",
  8. "members" : [
  9. {
  10. "_id" : ,
  11. "host" : "10.202.11.117:27017"
  12. },
  13. {
  14. "_id" : ,
  15. "host" : "10.202.11.118:27017"
  16. },
  17. {
  18. "_id" : ,
  19. "host" : "10.202.37.75:27017"
  20. }
  21. ]
  22. }
#初始化副本集配置

  1. #初始化副本集配置

> rs.initiate(config);
{ "ok" : 1 }
repset:OTHER>

注意上面的变化,标红部分。

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

  1. --29T11::07.678+ I REPL [rsSync] transition to SECONDARY
  2. --29T11::09.470+ I NETWORK [thread1] connection accepted from 10.202.11.118: # ( connections now open)
  3. --29T11::09.470+ I - [conn6] end connection 10.202.11.118: ( connections now open)
  4. --29T11::09.483+ I NETWORK [thread1] connection accepted from 10.202.37.75: # ( connections now open)
  5. --29T11::09.483+ I - [conn7] end connection 10.202.37.75: ( connections now open)
  6. --29T11::09.775+ I NETWORK [thread1] connection accepted from 10.202.11.118: # ( connections now open)
  7. --29T11::09.775+ I NETWORK [conn8] received client metadata from 10.202.11.118: conn8: { driver: { name: "NetworkInterfaceASIO-RS", version: "3.4.2" }, os: { type: "Linux", name: "CentOS release 6.6 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-504.el6.x86_64" } }
  8. --29T11::09.777+ I NETWORK [thread1] connection accepted from 10.202.11.118: # ( connections now open)
  9. --29T11::09.778+ I NETWORK [conn9] received client metadata from 10.202.11.118: conn9: { driver: { name: "NetworkInterfaceASIO-RS", version: "3.4.2" }, os: { type: "Linux", name: "CentOS release 6.6 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-504.el6.x86_64" } }
  10. --29T11::10.153+ I NETWORK [thread1] connection accepted from 10.202.37.75: # ( connections now open)
  11. --29T11::10.153+ I NETWORK [conn10] received client metadata from 10.202.37.75: conn10: { driver: { name: "NetworkInterfaceASIO-RS", version: "3.4.2" }, os: { type: "Linux", name: "CentOS release 6.6 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-504.el6.x86_64" } }
  12. --29T11::10.156+ I NETWORK [thread1] connection accepted from 10.202.37.75: # ( connections now open)
  13. --29T11::10.157+ I NETWORK [conn11] received client metadata from 10.202.37.75: conn11: { driver: { name: "NetworkInterfaceASIO-RS", version: "3.4.2" }, os: { type: "Linux", name: "CentOS release 6.6 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-504.el6.x86_64" } }
  14. --29T11::12.676+ I REPL [ReplicationExecutor] Member 10.202.11.118: is now in state SECONDARY
  15. --29T11::12.677+ I REPL [ReplicationExecutor] Member 10.202.37.75: is now in state SECONDARY
  16. --29T11::14.778+ I - [conn9] end connection 10.202.11.118: ( connections now open)
  17. --29T11::15.159+ I - [conn11] end connection 10.202.37.75: ( connections now open)
  18. --29T11::17.996+ I REPL [ReplicationExecutor] Starting an election, since we've seen no PRIMARY in the past 10000ms
  19. --29T11::17.996+ I REPL [ReplicationExecutor] conducting a dry run election to see if we could be elected
  20. --29T11::17.996+ I REPL [ReplicationExecutor] VoteRequester(term dry run) received a yes vote from 10.202.11.118:; response message: { term: , voteGranted: true, reason: "", ok: 1.0 }
  21. --29T11::17.997+ I REPL [ReplicationExecutor] dry election run succeeded, running for election
  22. --29T11::18.039+ I ASIO [NetworkInterfaceASIO-Replication-] Connecting to 10.202.37.75:
  23. --29T11::18.044+ I ASIO [NetworkInterfaceASIO-Replication-] Successfully connected to 10.202.37.75:
  24. --29T11::18.100+ I REPL [ReplicationExecutor] VoteRequester(term ) received a yes vote from 10.202.11.118:; response message: { term: , voteGranted: true, reason: "", ok: 1.0 }

#查看集群节点的状态

  1. #查看集群节点的状态

repset:SECONDARY> rs.status()
{
"set" : "repset",
"date" : ISODate("2017-03-29T03:33:14.286Z"),
"myState" : 1,
"term" : NumberLong(1),
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1490758388, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1490758388, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1490758388, 1),
"t" : NumberLong(1)
}
},
"members" : [
{
"_id" : 0,
"name" : "10.202.11.117:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 644,
"optime" : {
"ts" : Timestamp(1490758388, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2017-03-29T03:33:08Z"),
"electionTime" : Timestamp(1490757918, 1),
"electionDate" : ISODate("2017-03-29T03:25:18Z"),
"configVersion" : 1,
"self" : true
},
{
"_id" : 1,
"name" : "10.202.11.118:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 486,
"optime" : {
"ts" : Timestamp(1490758388, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1490758388, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2017-03-29T03:33:08Z"),
"optimeDurableDate" : ISODate("2017-03-29T03:33:08Z"),
"lastHeartbeat" : ISODate("2017-03-29T03:33:14.282Z"),
"lastHeartbeatRecv" : ISODate("2017-03-29T03:33:13.087Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.202.11.117:27017",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "10.202.37.75:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 486,
"optime" : {
"ts" : Timestamp(1490758388, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1490758388, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2017-03-29T03:33:08Z"),
"optimeDurableDate" : ISODate("2017-03-29T03:33:08Z"),
"lastHeartbeat" : ISODate("2017-03-29T03:33:12.377Z"),
"lastHeartbeatRecv" : ISODate("2017-03-29T03:33:13.665Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.202.11.118:27017",
"configVersion" : 1
}
],
"ok" : 1
}
repset:PRIMARY>

  1.  
 

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

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

  1. #在主节点10.202.11.117上连接到终端:
  2. #建立tong 数据库。
  3. #往testdb表插入数据。
  4. repset:PRIMARY> use tong
  5. switched to db tong
  6. repset:PRIMARY> show collections;
  7. repset:PRIMARY> show collections;
  8. repset:PRIMARY> db.testdb.insert({"name":"shenzhen","addr":"nanshan"})
  9. WriteResult({ "nInserted" : 1 })
  10. repset:PRIMARY>
  11.  
  12. #在副本节点 10.202.11.118,10.202.37.75 上连接到mongodb查看数据是否复制过来。
  13. ./mongo
  14. #使用tong 数据库。
  15. repset:SECONDARY> use tong
  16. switched to db tong
  17. repset:SECONDARY> db.testdb.find()
  18. Error: error: {
  19. "ok" : 0,
  20. "errmsg" : "not master and slaveOk=false",
  21. "code" : 13435,
  22. "codeName" : "NotMasterNoSlaveOk"
  23. }
  24. repset:SECONDARY> rs.slaveOk();
  25. repset:SECONDARY> db.testdb.find()
  26. { "_id" : ObjectId("58db2b6572cf3b348b3cc0f5"), "name" : "shenzhen", "addr" : "nanshan" }
  27. repset:SECONDARY> show tables;
  28. testdb
  29. repset:SECONDARY>
 

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

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

  1. --29T11::15.315+ I NETWORK [conn8] received client metadata from 127.0.0.1: conn8: { application: { name: "MongoDB Shell" }, driver: { name: "MongoDB Internal Client", version: "3.4.2" }, os: { type: "Linux", name: "CentOS release 6.6 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-504.el6.x86_64" } }
  2. --29T11::58.446+ I - [conn7] end connection 10.202.11.117: ( connections now open)
  3. --29T11::58.699+ I REPL [replication-] Choosing new sync source because our current sync source, 10.202.11.118:, has an OpTime ({ ts: Timestamp |, t: }) which is not ahead of ours ({ ts: Timestamp |, t: }), it does not have a sync source, and it's not the primary (sync source does not know the primary)
  4. 2017-03-29T11:38:58.699+0800 I REPL [replication-1] Canceling oplog query because we have to choose a new sync source. Current source: 10.202.11.118:27017, OpTime { ts: Timestamp 0|0, t: -1 }, its sync source index:-1
  5. 2017-03-29T11:38:58.699+0800 W REPL [rsBackgroundSync] Fetcher stopped querying remote oplog with error: InvalidSyncSource: sync source 10.202.11.118:27017 (last visible optime: { ts: Timestamp 0|0, t: -1 }; config version: 1; sync source index: -1; primary index: -1) is no longer valid
  6. 2017-03-29T11:38:58.700+0800 I REPL [rsBackgroundSync] could not find member to sync from
  7. 2017-03-29T11:38:58.700+0800 I ASIO [ReplicationExecutor] dropping unhealthy pooled connection to 10.202.11.117:27017
  8. 2017-03-29T11:38:58.700+0800 I ASIO [ReplicationExecutor] after drop, pool was empty, going to spawn some connections
  9. 2017-03-29T11:38:58.700+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Connecting to 10.202.11.117:27017
  10. 2017-03-29T11:38:58.702+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Failed to connect to 10.202.11.117:27017 - HostUnreachable: Connection refused
  11. 2017-03-29T11:38:58.702+0800 I REPL [ReplicationExecutor] Error in heartbeat request to 10.202.11.117:27017; HostUnreachable: Connection refused

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

  1. #在118上查看状态
  2. repset:SECONDARY> rs.status()
  3. {
  4. "set" : "repset",
  5. "date" : ISODate("2017-03-29T03:43:17.178Z"),
  6. "myState" : 2,
  7. "term" : NumberLong(2),
  8. "syncingTo" : "10.202.37.75:27017",
  9. "heartbeatIntervalMillis" : NumberLong(2000),
  10. "optimes" : {
  11. "lastCommittedOpTime" : {
  12. "ts" : Timestamp(1490758988, 1),
  13. "t" : NumberLong(2)
  14. },
  15. "appliedOpTime" : {
  16. "ts" : Timestamp(1490758988, 1),
  17. "t" : NumberLong(2)
  18. },
  19. "durableOpTime" : {
  20. "ts" : Timestamp(1490758988, 1),
  21. "t" : NumberLong(2)
  22. }
  23. },
  24. "members" : [
  25. {
  26. "_id" : 0,
  27. "name" : "10.202.11.117:27017",
  28. "health" : 0,
  29. "state" : 8,
  30. "stateStr" : "(not reachable/healthy)",
  31. "uptime" : 0,
  32. "optime" : {
  33. "ts" : Timestamp(0, 0),
  34. "t" : NumberLong(-1)
  35. },
  36. "optimeDurable" : {
  37. "ts" : Timestamp(0, 0),
  38. "t" : NumberLong(-1)
  39. },
  40. "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
  41. "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
  42. "lastHeartbeat" : ISODate("2017-03-29T03:43:16.754Z"),
  43. "lastHeartbeatRecv" : ISODate("2017-03-29T03:38:58.418Z"),
  44. "pingMs" : NumberLong(0),
  45. "lastHeartbeatMessage" : "Connection refused",
  46. "configVersion" : -1
  47. },
  48. {
  49. "_id" : 1,
  50. "name" : "10.202.11.118:27017",
  51. "health" : 1,
  52. "state" : 2,
  53. "stateStr" : "SECONDARY",
  54. "uptime" : 1184,
  55. "optime" : {
  56. "ts" : Timestamp(1490758988, 1),
  57. "t" : NumberLong(2)
  58. },
  59. "optimeDate" : ISODate("2017-03-29T03:43:08Z"),
  60. "syncingTo" : "10.202.37.75:27017",
  61. "configVersion" : 1,
  62. "self" : true
  63. },
  64. {
  65. "_id" : 2,
  66. "name" : "10.202.37.75:27017",
  67. "health" : 1,
  68. "state" : 1,
  69. "stateStr" : "PRIMARY",
  70. "uptime" : 1087,
  71. "optime" : {
  72. "ts" : Timestamp(1490758988, 1),
  73. "t" : NumberLong(2)
  74. },
  75. "optimeDurable" : {
  76. "ts" : Timestamp(1490758988, 1),
  77. "t" : NumberLong(2)
  78. },
  79. "optimeDate" : ISODate("2017-03-29T03:43:08Z"),
  80. "optimeDurableDate" : ISODate("2017-03-29T03:43:08Z"),
  81. "lastHeartbeat" : ISODate("2017-03-29T03:43:16.624Z"),
  82. "lastHeartbeatRecv" : ISODate("2017-03-29T03:43:16.271Z"),
  83. "pingMs" : NumberLong(0),
  84. "electionTime" : Timestamp(1490758748, 1),
  85. "electionDate" : ISODate("2017-03-29T03:39:08Z"),
  86. "configVersion" : 1
  87. }
  88. ],
  89. "ok" : 1
  90. }
  91. repset:SECONDARY>

在新的PRIMARY节点上新增一条记录,看SECONDARY节点能否同步过去。

  1. #.75上等上mongo
  2. ./mongo
  3. repset:PRIMARY> use tong
  4. switched to db tong
  5. repset:PRIMARY> db.testdb.find()
  6. { "_id" : ObjectId("58db2b6572cf3b348b3cc0f5"), "name" : "shenzhen", "addr" : "nanshan" }
  7. repset:PRIMARY> db.testdb.insert({"name":"37.75 primary","addr":"75"})
  8. WriteResult({ "nInserted" : 1 })
  9. repset:PRIMARY> db.testdb.find()
  10. { "_id" : ObjectId("58db2b6572cf3b348b3cc0f5"), "name" : "shenzhen", "addr" : "nanshan" }
  11. { "_id" : ObjectId("58db312b200c0b77a06fc328"), "name" : "37.75 primary", "addr" : "75" }
  12. repset:PRIMARY>

在10.202.11.118上查看同步结果:

  1. repset:SECONDARY> rs.slaveOk()
  2. repset:SECONDARY> use tong
  3. switched to db tong
  4. repset:SECONDARY> db.testdb.find()
  5. { "_id" : ObjectId("58db2b6572cf3b348b3cc0f5"), "name" : "shenzhen", "addr" : "nanshan" }
  6. { "_id" : ObjectId("58db312b200c0b77a06fc328"), "name" : "37.75 primary", "addr" : "75" }
  7. repset:SECONDARY>

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

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

要引入的jar有:

bson-3.4.2.jar,mongodb-driver-3.4.2.jar,mongodb-driver-core-3.4.2.jar

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. import com.mongodb.BasicDBObject;
  5. import com.mongodb.DB;
  6. import com.mongodb.DBCollection;
  7. import com.mongodb.DBCursor;
  8. import com.mongodb.DBObject;
  9. import com.mongodb.MongoClient;
  10. import com.mongodb.ServerAddress;
  11.  
  12. public class TestMongoDBReplSet {
  13.  
  14. public static void main(String[] args) {
  15.  
  16. try {
  17. List<ServerAddress> addresses = new ArrayList<ServerAddress>();
  18. ServerAddress address1 = new ServerAddress("10.202.11.117", 27017);
  19. ServerAddress address2 = new ServerAddress("10.202.37.75", 27017);
  20. ServerAddress address3 = new ServerAddress("10.202.11.118", 27017);
  21. addresses.add(address1);
  22. addresses.add(address2);
  23. addresses.add(address3);
  24.  
  25. MongoClient client = new MongoClient(addresses);
  26. DB db = client.getDB("tong");
  27. DBCollection coll = db.getCollection("testdb");
  28.  
  29. // 鎻掑叆
  30. BasicDBObject object = new BasicDBObject();
  31. Object obj = new Object();
  32. obj = "value";
  33. object.append("test", obj);
  34. coll.insert(object);
  35.  
  36. DBCursor dbCursor = coll.find();
  37.  
  38. while (dbCursor.hasNext()) {
  39. DBObject dbObject = dbCursor.next();
  40. System.out.println(dbObject.toString());
  41. }
  42.  
  43. } catch (Exception e) {
  44. e.printStackTrace();
  45. }
  46.  
  47. }
  48.  
  49. }

结果:

  1. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  2. 信息: Cluster created with settings {hosts=[10.202.11.117:, 10.202.37.75:, 10.202.11.118:], mode=MULTIPLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=}
  3. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  4. 信息: Adding discovered server 10.202.11.117: to client view of cluster
  5. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  6. 信息: Adding discovered server 10.202.37.75: to client view of cluster
  7. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  8. 信息: Adding discovered server 10.202.11.118: to client view of cluster
  9. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  10. 信息: No server chosen by WritableServerSelector from cluster description ClusterDescription{type=UNKNOWN, connectionMode=MULTIPLE, serverDescriptions=[ServerDescription{address=10.202.11.118:, type=UNKNOWN, state=CONNECTING}, ServerDescription{address=10.202.11.117:, type=UNKNOWN, state=CONNECTING}, ServerDescription{address=10.202.37.75:, type=UNKNOWN, state=CONNECTING}]}. Waiting for ms before timing out
  11. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  12. 信息: Opened connection [connectionId{localValue:, serverValue:}] to 10.202.37.75:
  13. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  14. 信息: Opened connection [connectionId{localValue:, serverValue:}] to 10.202.11.117:
  15. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  16. 信息: Opened connection [connectionId{localValue:, serverValue:}] to 10.202.11.118:
  17. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  18. 信息: Monitor thread successfully connected to server with description ServerDescription{address=10.202.11.117:, type=REPLICA_SET_SECONDARY, state=CONNECTED, ok=true, version=ServerVersion{versionList=[, , ]}, minWireVersion=, maxWireVersion=, maxDocumentSize=, roundTripTimeNanos=, setName='repset', canonicalAddress=10.202.11.117:, hosts=[10.202.37.75:, 10.202.11.117:, 10.202.11.118:], passives=[], arbiters=[], primary='10.202.37.75:27017', tagSet=TagSet{[]}, electionId=null, setVersion=, lastWriteDate=Wed Mar :: CST , lastUpdateTimeNanos=}
  19. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  20. 信息: Monitor thread successfully connected to server with description ServerDescription{address=10.202.37.75:, type=REPLICA_SET_PRIMARY, state=CONNECTED, ok=true, version=ServerVersion{versionList=[, , ]}, minWireVersion=, maxWireVersion=, maxDocumentSize=, roundTripTimeNanos=, setName='repset', canonicalAddress=10.202.37.75:, hosts=[10.202.37.75:, 10.202.11.117:, 10.202.11.118:], passives=[], arbiters=[], primary='10.202.37.75:27017', tagSet=TagSet{[]}, electionId=7fffffff0000000000000002, setVersion=, lastWriteDate=Wed Mar :: CST , lastUpdateTimeNanos=}
  21. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  22. 信息: Discovered cluster type of REPLICA_SET
  23. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  24. 信息: Monitor thread successfully connected to server with description ServerDescription{address=10.202.11.118:, type=REPLICA_SET_SECONDARY, state=CONNECTED, ok=true, version=ServerVersion{versionList=[, , ]}, minWireVersion=, maxWireVersion=, maxDocumentSize=, roundTripTimeNanos=, setName='repset', canonicalAddress=10.202.11.118:, hosts=[10.202.37.75:, 10.202.11.117:, 10.202.11.118:], passives=[], arbiters=[], primary='10.202.37.75:27017', tagSet=TagSet{[]}, electionId=null, setVersion=, lastWriteDate=Wed Mar :: CST , lastUpdateTimeNanos=}
  25. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  26. 信息: Setting max election id to 7fffffff0000000000000002 from replica set primary 10.202.37.75:
  27. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  28. 信息: Setting max set version to from replica set primary 10.202.37.75:
  29. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  30. 信息: Discovered replica set primary 10.202.37.75:
  31. 三月 , :: 下午 com.mongodb.diagnostics.logging.JULLogger log
  32. 信息: Opened connection [connectionId{localValue:, serverValue:}] to 10.202.37.75:
  33. { "_id" : { "$oid" : "58db2b6572cf3b348b3cc0f5"} , "name" : "shenzhen" , "addr" : "nanshan"}
  34. { "_id" : { "$oid" : "58db312b200c0b77a06fc328"} , "name" : "37.75 primary" , "addr" : "75"}
  35. { "_id" : { "$oid" : "58db5075524e6107003d9978"} , "test" : "value"}

用mongo的shell命令查看结果:

  1. repset:SECONDARY> db.testdb.find()
  2. { "_id" : ObjectId("58db2b6572cf3b348b3cc0f5"), "name" : "shenzhen", "addr" : "nanshan" }
  3. { "_id" : ObjectId("58db312b200c0b77a06fc328"), "name" : "37.75 primary", "addr" : "75" }
  4. { "_id" : ObjectId("58db5075524e6107003d9978"), "test" : "value" }
  5. repset:SECONDARY>

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

看图说话:

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

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

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. import com.mongodb.BasicDBObject;
  5. import com.mongodb.DB;
  6. import com.mongodb.DBCollection;
  7. import com.mongodb.DBObject;
  8. import com.mongodb.MongoClient;
  9. import com.mongodb.ReadPreference;
  10. import com.mongodb.ServerAddress;
  11.  
  12. public class TestMongoDBReplSetReadSplit {
  13.  
  14. public static void main(String[] args) {
  15.  
  16. try {
  17. List<ServerAddress> addresses = new ArrayList<ServerAddress>();
  18. ServerAddress address1 = new ServerAddress("10.202.11.117", 27017);
  19. ServerAddress address2 = new ServerAddress("10.202.37.75", 27017);
  20. ServerAddress address3 = new ServerAddress("10.202.11.118", 27017);
  21. addresses.add(address1);
  22. addresses.add(address2);
  23. addresses.add(address3);
  24.  
  25. MongoClient client = new MongoClient(addresses);
  26. DB db = client.getDB("tong");
  27. DBCollection coll = db.getCollection("testdb");
  28.  
  29. BasicDBObject object = new BasicDBObject();
  30. object.append("test", "value");
  31.  
  32. // 读操作从副本节点读取
  33. ReadPreference preference = ReadPreference.secondary();
  34. DBObject dbObject = coll.findOne(object, null, preference);
  35.  
  36. System.out.println(dbObject);
  37.  
  38. } catch (Exception e) {
  39. e.printStackTrace();
  40. }
  41. }
  42. }

结果:

  1. 信息: Discovered replica set primary 10.202.37.75:27017
  2. 三月 29, 2017 2:18:34 下午 com.mongodb.diagnostics.logging.JULLogger log
  3. 信息: Opened connection [connectionId{localValue:4, serverValue:17}] to 10.202.11.118:27017
  4. { "_id" : { "$oid" : "58db5075524e6107003d9978"} , "test" : "value"}

读参数除了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

android改变字体的颜色的三种方法的更多相关文章

  1. Android中设置文本颜色的三种方法

    最近刚开始学web,发现好的颜色搭配可以让自己的网页更加美观, 中午不想做事,就无聊滴花了两个小时测试了所有颜色的编码,总结如下 新手没有什么吊炸天的技术,仅仅是一份辅助的文档,有兴趣的朋友可以收藏下 ...

  2. Android 改变字体颜色的三种方法

    在TextView中添加文本时有时需要改变一些文本字体的颜色,今天学到了三种方法,拿出来分享一下     1.在layout文件下的配置xml文件中直接设置字体颜色,通过添加android:textc ...

  3. Android开发之去掉标题栏的三种方法,推荐第三种

    Android:去掉标题栏的三种方法和全屏的三种方法 第一种:一般入门的时候常常使用的一种方法 onCreate函数中增加下面代码: requestWindowFeature(Window.FEATU ...

  4. VC 对话框背景颜色、控件颜色(三种方法)

    系统环境:Windows 7软件环境:Visual C++ 2008 SP1本次目的:为对话框设置背景颜色.控件颜色 既然MFC对话框不好开发,那么现在我们来开始美化我们的对话框.为对话框设置背景颜色 ...

  5. Android开发中完全退出程序的三种方法

    参考: http://android.tgbus.com/Android/tutorial/201108/363511.shtml Android程序有很多Activity,比如说主窗口A,调用了子窗 ...

  6. android 设置颜色的三种方法

    1.利于系统自带的颜色类 如TextView1.setTextColor(Android.graphics.Color.RED); 2.数字颜色表示法 TextView1.setTextColor(0 ...

  7. Android Studio导入第三方库的三种方法

    叨叨在前 今天在项目中使用一个图片选择器的第三方框架——GalleryFinal,想要导入源码,以便于修改,于是上完查找了一下方法,想到之前用到过其他导入第三方库的方法,现在做个小总结,以防忘记. A ...

  8. Android 再按一次退出程序三种办法

    在Xamarin android中双击返回键退出程序的第一种做法 思路就是当用户按下返回键的时间超过两秒就退出,根据Keycode.Back判断用户按下的是返回键,重写这个OnKeyDown Date ...

  9. [Android] Android 异步定时任务实现的三种方法(以SeekBar的进度自动实现为例)

    [Android] Android 定时异步任务实现的三种方法(以SeekBar的进度自动实现为例) 一.采用Handler与线程的sleep(long)方法 二.采用Handler与timer及Ti ...

随机推荐

  1. HDU 3397 Sequence operation (区间合并,操作比较多)

    费了我一天半的时间,到处debug,后来才发现,主要是建树的时候只在叶子节点对lazy1和lazy2进行初始化了,父节点都没初始化...晕. 具体见代码吧. #include <iostream ...

  2. 无废话版本-Asp.net MVC4.0 Rasor的基本用法

    最近工作有点忙,好久没写东西了!废话不多说了,进入主题! 1.在页面中输出单一变量时候,只要在C#语句之前加上@符号即可,For example: <p>Now Time:@DateTim ...

  3. JsRender系列demo(4)-if else

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  4. hdu 1133 Buy the Ticket

    首先,记50的为0,100的为1. 当m=4,n=3时,其中的非法序列有0110010; 从不合法的1后面开始,0->1,1->0,得到序列式0111101 也就是说,非法序列变为了n-1 ...

  5. 《HTTP权威指南》笔记

    http://blog.csdn.net/sunorry?viewmode=contents有些笔记 MIME 类型是一种文本标记,表示一种主要的对象类型和一个特定的子类型,中间由一条斜杠来分隔:te ...

  6. 【Linux高频命令专题(12)】touch.md

    概述 一般在使用make的时候可能会用到,用来修改文件时间,或者新建一个不存在的文件. 命令格式 touch [选项]... 文件... 命令参数 -a 或--time=atime或--time=ac ...

  7. asp.net如何设置数据库连接池的数量

    http://www.cnblogs.com/wbcms/archive/2008/10/11/1308725.html 可以使用一组名称-值对以链接字符串的形式配置链接池.例如,可以配置池是否有效( ...

  8. Entity Framework: Get mapped table name from an entity

    The extension methods I have created one extension method for DbContext and other for ObjectContext: ...

  9. 248. Strobogrammatic Number III

    题目: A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at ups ...

  10. uboot---linux

    01uboot是没有虚拟地址的 02内存映射是linux内核的机制,也就是从实地址到虚拟地址是linux完成的! -----