Mongo副本集

本文的示例代碼參考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" }

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章