MongoDB 複製(副本集)

一、複製集相關概念

複製集
    複製是在多臺服務器之間同步數據的過程,由一組Mongod實例(進程)組成,包含一個Primary節點和多個Secondary節點
    Mongodb Driver(客戶端)的所有數據都寫入Primary,Secondary從Primary同步寫入的數據
    通過上述方式來保持複製集內所有成員存儲相同的數據集,提供數據的高可用

複製的目的
        Failover (故障轉移,故障切換,故障恢復)
        Redundancy(數據冗餘)
        避免單點,用於災難時恢復,報表處理,提升數據可用性
        讀寫分離,分擔讀壓力
        對用戶透明的系統維護升級

複製集的原理
        主節點記錄所有的變更到oplog日誌
        輔助節點(Secondary)複製主節點的oplog日誌並且將這些日誌在輔助節點進行重放(做)
        各個節點之間會定期發送心跳信息,一旦主節點宕機,則觸發選舉一個新的主節點,剩餘的輔助節點指向新的主
        10s內各輔助節點無法感知主節點的存在,則開始觸發選舉
        通常1分鐘內完成主輔助節點切換,10-30s內感知主節點故障,10-30s內完成選舉及切換       

複製≠備份
        用戶恢復數據,防止數據丟失,實現災難恢復
        人爲誤操作導致數據刪除,程序Bug導致數據損壞等

Primary
        首要複製節點,由選舉產生,提供讀寫服務的節點,產生oplog日誌

Secondary       
        備用(輔助)複製節點,Secondary可以提供讀服務,增加Secondary節點可以提供複製集的讀服務能力
        在故障時,備用節點可以根據設定的優先級別提升爲首要節點。提升了複製集的可用性

Arbiter
        Arbiter節點只參與投票,不能被選爲Primary,並且不從Primary同步數據     
        Arbiter本身不存儲數據,是非常輕量級的服務。
        當複製集成員爲偶數時,最好加入一個Arbiter節點,以提升複製集可用性

Screenshot from 2018-07-24 09-19-48.png

二、創建複製集

設置環境變量:

[root@server1 ~]# vim ~/.bashrc
Screenshot from 2018-07-24 09-33-51.png

[root@server1 ~]# source ~/.bashrc

簡述環境變量的區別:

修改方法一

export PATH=/usr/local/mongodb/bin:$PATH
//配置完後可以通過echo $PATH查看配置結果。
生效方法:立即生效
有效期限:臨時改變,只能在當前的終端窗口中有效,當前窗口關閉後就會恢復原有的path配置
用戶侷限:僅對當前用戶

修改方法二

通過修改.bashrc文件:
vim ~/.bashrc  
//在最後一行添上:
export PATH=/usr/local/mongodb/bin:$PATH
生效方法:(有以下兩種)
1、關閉當前終端窗口,重新打開一個新終端窗口就能生效
2、輸入“source ~/.bashrc”命令,立即生效
有效期限:永久有效
用戶侷限:僅對當前用戶

修改方法三

通過修改profile文件:
vim /etc/profile
/export PATH //找到設置PATH的行,添加
export PATH=/usr/local/mongodb/bin:$PATH
生效方法:系統重啓
有效期限:永久有效
用戶侷限:對所有用戶


創建實例對應的數據目錄

[root@server1 ~]# mkdir /home/data/{n1,n2,n3}


Screenshot from 2018-07-24 09-26-26.png


[root@server1 ~]# mongod --replSet repSetTest --dbpath /home/data/n1 --logpath /home/data/n1/n1.log --port 27000 --smallfiles --oplogSize 128 --fork

[root@server1 ~]# mongod --replSet repSetTest --dbpath /home/data/n2 --logpath /home/data/n2/n2.log --port 27001 --smallfiles --oplogSize 128 --fork

[root@server1 ~]# mongod --replSet repSetTest --dbpath /home/data/n3 --logpath /home/data/n3/n3.log --port 27002 --smallfiles --oplogSize 128 --fork

查看相應的端口

Screenshot from 2018-07-24 09-39-25.png


連接到第一個實例

[root@server1 ~]# mongo localhost:27000


> db.person.insert({name:'cara',age:22})      //提示當前節點非master節點

WriteCommandError({
    "operationTime" : Timestamp(0, 0),
    "ok" : 0,
    "errmsg" : "not master",
    "code" : 10107,
    "codeName" : "NotMaster",
    "$clusterTime" : {
        "clusterTime" : Timestamp(0, 0),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
})


//下面我們添加複製集的配置文件

> cfg = {
... '_id':'repSetTest',
... 'members':[
... {'_id':0, 'host': 'localhost:27000'},
... {'_id':1, 'host': 'localhost:27001'},
... {'_id':2, 'host': 'localhost:27002'}
... ]
... }
{
    "_id" : "repSetTest",
    "members" : [
        {
            "_id" : 0,
            "host" : "localhost:27000"
        },
        {
            "_id" : 1,
            "host" : "localhost:27001"
        },
        {
            "_id" : 2,
            "host" : "localhost:27002"
        }
    ]
}
>

//複製集通過replSetInitiate命令(或mongo shell的rs.initiate())進行初始化
//初始化後各個成員間開始發送心跳消息,併發起Priamry選舉操作
//獲得『大多數』成員投票支持的節點,會成爲Primary,其餘節點成爲Secondary。
//通常建議將複製集成員數量設置爲奇數,以確保在複製集故障的時候能夠正確選舉出Primary。
//對於複製集故障導致無法正確選舉得到Primary的情形下,複製集將無法提供寫服務,處於只讀狀態
> rs.initiate(cfg)          //初始化配置文件

{
    "ok" : 1,
    "operationTime" : Timestamp(1532397006, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1532397006, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}
repSetTest:SECONDARY> 


//查看狀態,以下提示27000爲主節點,其餘2個端口爲輔助節點

repSetTest:SECONDARY> rs.status()

{
    "set" : "repSetTest",
    "date" : ISODate("2018-07-24T01:51:46.017Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "syncingTo" : "",
    "syncSourceHost" : "",
    "syncSourceId" : -1,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1532397099, 1),
            "t" : NumberLong(1)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1532397099, 1),
            "t" : NumberLong(1)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1532397099, 1),
            "t" : NumberLong(1)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1532397099, 1),
            "t" : NumberLong(1)
        }
    },
    "lastStableCheckpointTimestamp" : Timestamp(1532397079, 1),
    "members" : [
        {
            "_id" : 0,
            "name" : "localhost:27000",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",         // 主節點
            "uptime" : 873,
            "optime" : {
                "ts" : Timestamp(1532397099, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-07-24T01:51:39Z"),
            "syncingTo" : "",
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "could not find member to sync from",
            "electionTime" : Timestamp(1532397017, 1),
            "electionDate" : ISODate("2018-07-24T01:50:17Z"),
            "configVersion" : 1,
            "self" : true,
            "lastHeartbeatMessage" : ""
        },
        {
            "_id" : 1,
            "name" : "localhost:27001",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 99,
            "optime" : {
                "ts" : Timestamp(1532397099, 1),
                "t" : NumberLong(1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1532397099, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-07-24T01:51:39Z"),
            "optimeDurableDate" : ISODate("2018-07-24T01:51:39Z"),
            "lastHeartbeat" : ISODate("2018-07-24T01:51:45.928Z"),
            "lastHeartbeatRecv" : ISODate("2018-07-24T01:51:44.242Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "",
            "syncingTo" : "localhost:27000",
            "syncSourceHost" : "localhost:27000",
            "syncSourceId" : 0,
            "infoMessage" : "",
            "configVersion" : 1
        },
        {
            "_id" : 2,
            "name" : "localhost:27002",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 99,
            "optime" : {
                "ts" : Timestamp(1532397099, 1),
                "t" : NumberLong(1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1532397099, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-07-24T01:51:39Z"),
            "optimeDurableDate" : ISODate("2018-07-24T01:51:39Z"),
            "lastHeartbeat" : ISODate("2018-07-24T01:51:45.928Z"),
            "lastHeartbeatRecv" : ISODate("2018-07-24T01:51:44.208Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "",
            "syncingTo" : "localhost:27000",
            "syncSourceHost" : "localhost:27000",
            "syncSourceId" : 0,
            "infoMessage" : "",
            "configVersion" : 1
        }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1532397099, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1532397099, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}


//使用isMaster()函數尋找誰是Master

repSetTest:PRIMARY> db.isMaster()
{
    "hosts" : [
        "localhost:27000",
        "localhost:27001",
        "localhost:27002"
    ],
    "setName" : "repSetTest",
    "setVersion" : 1,
    "ismaster" : true,
    "secondary" : false,
    "primary" : "localhost:27000",
    "me" : "localhost:27000",
    "electionId" : ObjectId("7fffffff0000000000000001"),
    "lastWrite" : {
        "opTime" : {
            "ts" : Timestamp(1532397409, 1),
            "t" : NumberLong(1)
        },
        "lastWriteDate" : ISODate("2018-07-24T01:56:49Z"),
        "majorityOpTime" : {
            "ts" : Timestamp(1532397409, 1),
            "t" : NumberLong(1)
        },
        "majorityWriteDate" : ISODate("2018-07-24T01:56:49Z")
    },
    "maxBsonObjectSize" : 16777216,
    "maxMessageSizeBytes" : 48000000,
    "maxWriteBatchSize" : 100000,
    "localTime" : ISODate("2018-07-24T01:56:59.218Z"),
    "logicalSessionTimeoutMinutes" : 30,
    "minWireVersion" : 0,
    "maxWireVersion" : 7,
    "readOnly" : false,
    "ok" : 1,
    "operationTime" : Timestamp(1532397409, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1532397409, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

//在主複製集上插入文檔

repSetTest:PRIMARY> db.replTest.insert({_id:1,value:'abc'})
WriteResult({ "nInserted" : 1 })

查看:

repSetTest:PRIMARY> db.replTest.findOne()
{ "_id" : 1, "value" : "abc" }

//連接到從庫查詢,提示not master

[root@server1 ~]# mongo localhost:27001

//開啓slave查詢

repSetTest:SECONDARY> rs.slaveOk(true)
repSetTest:SECONDARY> db.replTest.find()
{ "_id" : 1, "value" : "abc" }

三、複製集自動故障轉移

此時,kill掉27000的進程


Screenshot from 2018-07-24 10-44-30.png


[root@server1 ~]# kill -9 2098

再次連接會被拒絕

[root@server1 ~]# mongo localhost:27000
MongoDB shell version v4.0.0
connecting to: mongodb://localhost:27000/test
2018-07-24T10:44:22.435+0800 E QUERY    [js] Error: couldn't connect to server localhost:27000, connection attempt failed: SocketException: Error connecting to localhost:27000 (127.0.0.1:27000) :: caused by :: Connection refused :
connect@src/mongo/shell/mongo.js:251:13
@(connect):1:6
exception: connect failed

//連接到27001端口,如下面的查詢,27000連接失敗,27002已經提升爲PRIMARY

[root@server1 ~]# mongo localhost:27001

repSetTest:SECONDARY> rs.status()
{
    "set" : "repSetTest",
    "date" : ISODate("2018-07-24T02:46:40.162Z"),
    "myState" : 2,
    "term" : NumberLong(2),
    "syncingTo" : "localhost:27002",
    "syncSourceHost" : "localhost:27002",
    "syncSourceId" : 2,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1532400396, 1),
            "t" : NumberLong(2)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1532400396, 1),
            "t" : NumberLong(2)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1532400396, 1),
            "t" : NumberLong(2)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1532400396, 1),
            "t" : NumberLong(2)
        }
    },
    "lastStableCheckpointTimestamp" : Timestamp(1532400376, 1),
    "members" : [
        {
            "_id" : 0,
            "name" : "localhost:27000",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",      // 提示27001不可到達
            "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-07-24T02:46:40.048Z"),
            "lastHeartbeatRecv" : ISODate("2018-07-24T02:43:56.679Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "Error connecting to localhost:27000 (127.0.0.1:27000) :: caused by :: Connection refused",
            "syncingTo" : "",
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "",
            "configVersion" : -1
        },
        {
            "_id" : 1,
            "name" : "localhost:27001",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 4137,
            "optime" : {
                "ts" : Timestamp(1532400396, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2018-07-24T02:46:36Z"),
            "syncingTo" : "localhost:27002",
            "syncSourceHost" : "localhost:27002",
            "syncSourceId" : 2,
            "infoMessage" : "",
            "configVersion" : 1,
            "self" : true,
            "lastHeartbeatMessage" : ""
        },
        {
            "_id" : 2,
            "name" : "localhost:27002",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",           //27002成爲PRIMARY

            "uptime" : 3391,
            "optime" : {
                "ts" : Timestamp(1532400396, 1),
                "t" : NumberLong(2)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1532400396, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2018-07-24T02:46:36Z"),
            "optimeDurableDate" : ISODate("2018-07-24T02:46:36Z"),
            "lastHeartbeat" : ISODate("2018-07-24T02:46:39.798Z"),
            "lastHeartbeatRecv" : ISODate("2018-07-24T02:46:38.901Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "",
            "syncingTo" : "",
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "",
            "electionTime" : Timestamp(1532400244, 1),
            "electionDate" : ISODate("2018-07-24T02:44:04Z"),
            "configVersion" : 1
        }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1532400396, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1532400396, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

//重新啓動27000實例

[root@server1 ~]# mongod --replSet repSetTest --dbpath /home/data/n1 --logpath /home/data/n1/n1.log --port 27000 --smallfiles --oplogSize 128 --fork
2018-07-24T10:51:56.523+0800 I CONTROL  [main] Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'
about to fork child process, waiting until server is ready for connections.
forked process: 2598
child process started successfully, parent exiting

//再次查看複製集的狀態,此時27000爲輔助副本

"members" : [
        {
            "_id" : 0,
            "name" : "localhost:27000",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",      // 此節點變成了輔助節點
            "uptime" : 28,
            "optime" : {
                "ts" : Timestamp(1532400746, 1),
                "t" : NumberLong(2)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1532400746, 1),
                "t" : NumberLong(2)
            },

四、獲取複製集的幫助

//獲取副本集相關的幫助命令

repSetTest:SECONDARY> rs.help()
    rs.status()                                { replSetGetStatus : 1 } checks repl set status
    rs.initiate()                              { replSetInitiate : null } initiates set with default settings
    rs.initiate(cfg)                           { replSetInitiate : cfg } initiates set with configuration cfg
    rs.conf()                                  get the current configuration object from local.system.replset
    rs.reconfig(cfg)                           updates the configuration of a running replica set with cfg (disconnects)
    rs.add(hostportstr)                        add a new member to the set with default attributes (disconnects)
    rs.add(membercfgobj)                       add a new member to the set with extra attributes (disconnects)
    rs.addArb(hostportstr)                     add a new member which is arbiterOnly:true (disconnects)
    rs.stepDown([stepdownSecs, catchUpSecs])   step down as primary (disconnects)
    rs.syncFrom(hostportstr)                   make a secondary sync from the given member
    rs.freeze(secs)                            make a node ineligible to become primary for the time specified
    rs.remove(hostportstr)                     remove a host from the replica set (disconnects)
    rs.slaveOk()                               allow queries on secondary nodes

    rs.printReplicationInfo()                  check oplog size and time range
    rs.printSlaveReplicationInfo()             check replica set members and replication lag
    db.isMaster()                              check who is primary

    reconfiguration helpers disconnect from the database so the shell will display
    an error, even if the command succeeds.
repSetTest:SECONDARY>

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