本文的示例代码参考MongoReplSet
目录
概念
复制集能力 = 主从复制 + 自动切换
最小复制集 = 1主 + 1从 + 1裁判 或者 1主 + 2从
搭建
mkdir ./primary ./secondary ./arbiter
mongod --replSet demo --dbpath ./primary --port 40000
mongod --replSet demo --dbpath ./secondary --port 40001
mongod --replSet demo --dbpath ./arbiter --port 40002
mongo --port 40000
use admin
rs.status()
{
"operationTime" : Timestamp(0, 0),
"ok" : 0,
"errmsg" : "no replset config has been received",
"code" : 94,
"codeName" : "NotYetInitialized",
"$clusterTime" : {
"clusterTime" : Timestamp(0, 0),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
config = {
"_id": "demo",
"members": [
{"_id": 0, "host": "127.0.0.1:40000"},
{"_id": 1, "host": "127.0.0.1:40001"},
{"_id": 2, "host": "127.0.0.1:40002", arbiterOnly: true}
]
}
rs.initiate(config)
rs.status()
{
"set" : "demo",
"date" : ISODate("2018-11-01T07:55:22.492Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1541058909, 1),
"t" : NumberLong(1)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1541058909, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1541058909, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1541058909, 1),
"t" : NumberLong(1)
}
},
"lastStableCheckpointTimestamp" : Timestamp(1541058907, 1),
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:40000",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 581,
"optime" : {
"ts" : Timestamp(1541058909, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-11-01T07:55:09Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "could not find member to sync from",
"electionTime" : Timestamp(1541058906, 1),
"electionDate" : ISODate("2018-11-01T07:55:06Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "127.0.0.1:40001",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 27,
"optime" : {
"ts" : Timestamp(1541058909, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1541058909, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2018-11-01T07:55:09Z"),
"optimeDurableDate" : ISODate("2018-11-01T07:55:09Z"),
"lastHeartbeat" : ISODate("2018-11-01T07:55:22.212Z"),
"lastHeartbeatRecv" : ISODate("2018-11-01T07:55:20.813Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:40000",
"syncSourceHost" : "127.0.0.1:40000",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "127.0.0.1:40002",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 27,
"lastHeartbeat" : ISODate("2018-11-01T07:55:22.212Z"),
"lastHeartbeatRecv" : ISODate("2018-11-01T07:55:21.154Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1541058909, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1541058909, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
测试
mongo --port 40000
use test_db
db.user.insert({'name': 'xiaowang'}) # WriteResult({ "nInserted" : 1 })
mongo --port 40001
use test_db
rs.slaveOk() # 默认的副本无法读写
db.user.find({}) # { "_id" : ObjectId("5bdfda1f21d260e60ff38c64"), "name" : "xiaowang" }
原理
oplog = operations log
heartbeat
rs.status() # 查看state状态
health 1 => good 0 => bad
state 1 => primary 2 => secondary 7 => arbiter
故障
mongo --port 40000
use admin
db.shutdownServer()
mongo --port 40001
rs.status() # 查看state状态
{
"set" : "demo",
"date" : ISODate("2018-11-01T08:07:44.667Z"),
"myState" : 1,
"term" : NumberLong(2),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1541059637, 1),
"t" : NumberLong(1)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1541059637, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1541059661, 1),
"t" : NumberLong(2)
},
"durableOpTime" : {
"ts" : Timestamp(1541059661, 1),
"t" : NumberLong(2)
}
},
"lastStableCheckpointTimestamp" : Timestamp(1541059627, 1),
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:40000",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : {
"ts" : Timestamp(0, 0),
"t" : NumberLong(-1)
},
"optimeDurable" : {
"ts" : Timestamp(0, 0),
"t" : NumberLong(-1)
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2018-11-01T08:07:44.001Z"),
"lastHeartbeatRecv" : ISODate("2018-11-01T08:07:19.105Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "Error connecting to 127.0.0.1:40000 :: caused by :: Connection refused",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"configVersion" : -1
},
{
"_id" : 1,
"name" : "127.0.0.1:40001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1093,
"optime" : {
"ts" : Timestamp(1541059661, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2018-11-01T08:07:41Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1541059639, 1),
"electionDate" : ISODate("2018-11-01T08:07:19Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 2,
"name" : "127.0.0.1:40002",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 767,
"lastHeartbeat" : ISODate("2018-11-01T08:07:44Z"),
"lastHeartbeatRecv" : ISODate("2018-11-01T08:07:44.143Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1541059661, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1541059661, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
health 1 => good 0 => bad
state 1 => primary 2 => secondary 7 => arbiter 8 => down
恢复
mongod --replSet demo --dbpath ./primary --port 40000
rs.status()
{
"set" : "demo",
"date" : ISODate("2018-11-01T08:11:23.967Z"),
"myState" : 1,
"term" : NumberLong(2),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1541059881, 1),
"t" : NumberLong(2)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1541059881, 1),
"t" : NumberLong(2)
},
"appliedOpTime" : {
"ts" : Timestamp(1541059881, 1),
"t" : NumberLong(2)
},
"durableOpTime" : {
"ts" : Timestamp(1541059881, 1),
"t" : NumberLong(2)
}
},
"lastStableCheckpointTimestamp" : Timestamp(1541059861, 1),
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:40000",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 17,
"optime" : {
"ts" : Timestamp(1541059881, 1),
"t" : NumberLong(2)
},
"optimeDurable" : {
"ts" : Timestamp(1541059881, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2018-11-01T08:11:21Z"),
"optimeDurableDate" : ISODate("2018-11-01T08:11:21Z"),
"lastHeartbeat" : ISODate("2018-11-01T08:11:22.757Z"),
"lastHeartbeatRecv" : ISODate("2018-11-01T08:11:23.711Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:40001",
"syncSourceHost" : "127.0.0.1:40001",
"syncSourceId" : 1,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 1,
"name" : "127.0.0.1:40001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1312,
"optime" : {
"ts" : Timestamp(1541059881, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2018-11-01T08:11:21Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1541059639, 1),
"electionDate" : ISODate("2018-11-01T08:07:19Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 2,
"name" : "127.0.0.1:40002",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 986,
"lastHeartbeat" : ISODate("2018-11-01T08:11:22.255Z"),
"lastHeartbeatRecv" : ISODate("2018-11-01T08:11:22.407Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1541059881, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1541059881, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
开发
副本
spring init -b 1.5.6.RELEASE -dweb,data-mongodb --build gradle MongoReplSet && cd MongoReplSet
vim src/main/resources/application.properties
spring.data.mongodb.uri=mongodb://localhost:40000,localhost:40001,localhost:40002/test_db?replicaSet=demo
vim src/main/java/com/example/MongoReplSet/User.java
package com.example.MongoReplSet;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "user")
public class User {
@Id
private String userId;
private String name;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
vim src/main/java/com/example/MongoReplSet/UserRepository.java
package com.example.MongoReplSet;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface UserRepository extends MongoRepository<User, String> {
List<User> findUsersByName(String name);
}
vim src/main/java/com/example/MongoReplSet/UsersController.java
package com.example.MongoReplSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UsersController {
@Autowired
UserRepository userRepository;
@PostMapping
public User create() {
User user = new User();
user.setName("xiaowang");
return userRepository.insert(user);
}
@GetMapping
public List<User> index() {
return userRepository.findUsersByName("xiaowang");
}
}
- 测试
./gradlew bootrun
curl localhost:8080/users | json
[
{
"userId": "5bdfda1f21d260e60ff38c64",
"name": "xiaowang"
}
]
故障
mongo --port 40000
use admin
db.shutdownServer()
2018-11-05 14:11:17.518 INFO 13653 --- [127.0.0.1:40001] org.mongodb.driver.cluster : Discovered replica set primary 127.0.0.1:40001
2018-11-05 14:11:17.518 INFO 13653 --- [127.0.0.1:40001] org.mongodb.driver.cluster : Rediscovering type of existing primary 127.0.0.1:40000
2018-11-05 14:11:17.523 INFO 13653 --- [127.0.0.1:40000] org.mongodb.driver.cluster : Exception in monitor thread while connecting to server 127.0.0.1:40000
com.mongodb.MongoSocketOpenException: Exception opening socket
at com.mongodb.connection.SocketStream.open(SocketStream.java:63) ~[mongodb-driver-core-3.4.2.jar:na]
at com.mongodb.connection.InternalStreamConnection.open(InternalStreamConnection.java:115) ~[mongodb-driver-core-3.4.2.jar:na]
at com.mongodb.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:127) ~[mongodb-driver-core-3.4.2.jar:na]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_144]
Caused by: java.net.ConnectException: Connection refused (Connection refused)
at java.net.PlainSocketImpl.socketConnect(Native Method) ~[na:1.8.0_144]
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[na:1.8.0_144]
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[na:1.8.0_144]
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[na:1.8.0_144]
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_144]
at java.net.Socket.connect(Socket.java:589) ~[na:1.8.0_144]
at com.mongodb.connection.SocketStreamHelper.initialize(SocketStreamHelper.java:57) ~[mongodb-driver-core-3.4.2.jar:na]
at com.mongodb.connection.SocketStream.open(SocketStream.java:58) ~[mongodb-driver-core-3.4.2.jar:na]
... 3 common frames omitted
2018-11-05 14:12:04.683 INFO 13653 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-11-05 14:12:04.683 INFO 13653 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2018-11-05 14:12:04.706 INFO 13653 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 23 ms
2018-11-05 14:12:04.760 INFO 13653 --- [nio-8080-exec-1] org.mongodb.driver.connection : Opened connection [connectionId{localValue:12, serverValue:36}] to 127.0.0.1:40001
- 测试
./gradlew bootrun
curl localhost:8080/users | json
[
{
"userId": "5bdfda1f21d260e60ff38c64",
"name": "xiaowang"
}
]
curl -X POST localhost:8080/users | json
{
"userId": "5bdfe570663117355596964a",
"name": "xiaowang"
}
curl localhost:8080/users | json
[
{
"userId": "5bdfda1f21d260e60ff38c64",
"name": "xiaowang"
},
{
"userId": "5bdfe570663117355596964a",
"name": "xiaowang"
}
]
恢复
mongod --replSet demo --dbpath ./primary --port 40000
mongo --port 40000
use test_db
rs.slaveOk() # 默认的副本无法读写
db.user.find({})
{ "_id" : ObjectId("5bdfda1f21d260e60ff38c64"), "name" : "xiaowang" }
{ "_id" : ObjectId("5bdfe570663117355596964a"), "_class" : "com.example.MongoReplSet.User", "name" : "xiaowang" }