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" }

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容