Mongodb主從複製/ 副本集/分片叢集介紹

散盡浮華發表於2016-07-19

 

前面的文章介紹了Mongodb的安裝使用,在 MongoDB 中,有兩種資料冗餘方式,一種 是 Master-Slave 模式(主從複製),一種是 Replica Sets 模式(副本集)。

Mongodb一共有三種叢集搭建的方式:
Replica Set(副本集)、
Sharding(切片)
Master-Slaver(主從)【目前已不推薦使用了!!!】

其中,Sharding叢集也是三種叢集中最複雜的。
副本集比起主從可以實現故障轉移!!非常使用!

mongoDB目前已不推薦使用主從模式,取而代之的是副本集模式。副本集其實一種互為主從的關係,可理解為主主。
副本集指將資料複製,多份儲存,不同伺服器儲存同一份資料,在出現故障時自動切換。對應的是資料冗餘、備份、映象、讀寫分離、高可用性等關鍵詞;
而分片則指為處理大量資料,將資料分開儲存,不同伺服器儲存不同的資料,它們的資料總和即為整個資料集。追求的是高效能。

在生產環境中,通常是這兩種技術結合使用,分片+副本集。

一、先說說mongodb主從複製配置

主從複製是MongoDB最常用的複製方式,也是一個簡單的資料庫同步備份的叢集技術,這種方式很靈活.可用於備份,故障恢復,讀擴充套件等. 
最基本的設定方式就是建立一個主節點和一個或多個從節點,每個從節點要知道主節點的地址。採用雙機備份後主節點掛掉了後從節點可以接替主機繼續服務。所以這種模式比單節點的高可用性要好很多。

 配置主從複製的注意點

1)在資料庫叢集中要明確的知道誰是主伺服器,主伺服器只有一臺.
2)從伺服器要知道自己的資料來源也就是對應的主服務是誰.
3)--master用來確定主伺服器,--slave 和 --source 來控制從伺服器

可以在mongodb.conf配置檔案裡指明主從關係,這樣啟動mongodb的時候只要跟上配置檔案就行,就不需要通過--master和--slave來指明主從了。

下面簡單記錄下Mongodb主從複製的部署過程

1)機器環境
182.48.115.238    master-node
182.48.115.236    slave-node

兩臺機器都關閉防火牆和selinux
mongodb的安裝參考:http://www.cnblogs.com/kevingrace/p/5752382.html

2)主從配置
.............master-node節點配置.............
[root@master-node ~]# vim /usr/local/mongodb/mongodb.conf
port=27017
bind_ip = 182.48.115.238
dbpath=/usr/local/mongodb/data
logpath=/usr/local/mongodb/log/mongo.log
logappend=true
journal = true
fork = true
master = true        //確定自己是主伺服器

[root@master-node ~]# nohup /usr/local/mongodb/bin/mongod --config /usr/local/mongodb/mongodb.conf &

[root@master-node ~]# ps -ef|grep mongodb
root     15707 15514 23 16:45 pts/2    00:00:00 /usr/local/mongodb/bin/mongod --config /usr/local/mongodb/mongodb.conf
root     15736 15514  0 16:45 pts/2    00:00:00 grep mongodb
[root@master-node ~]# lsof -i:27017
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mongod  15707 root    7u  IPv4 153114      0t0  TCP 182.48.115.238:27017 (LISTEN)

由於mongodb.conf裡繫結了本機的ip地址182.48.115.238,所以連線mongodb的時候必須用這個ip地址,不能使用預設的127.0.0.1,也就是說:
[root@master-node ~]# mongo 182.48.115.238:27017     //這樣才能連線mongodb
[root@master-node ~]# mongo 或者 mongodb 127.0.0.1:27017    // 這樣不能連線mongodb

.............slave-node節點配置.............
[root@slave-node ~]# vim /usr/local/mongodb/mongodb.conf 
port=27017
dbpath=/usr/local/mongodb/data
logpath=/usr/local/mongodb/log/mongo.log
logappend=true
journal = true
fork = true
bind_ip = 182.48.115.236            //確定主資料庫埠
source = 182.48.115.238:27017      //確定主資料庫埠
slave = true               //確定自己是從伺服器

[root@slave-node ~]# nohup /usr/local/mongodb/bin/mongod --config /usr/local/mongodb/mongodb.conf &

[root@slave-node ~]# ps -ef|grep mongo
root     26290 26126  8 16:47 pts/0    00:00:00 /usr/local/mongodb/bin/mongod --config /usr/local/mongodb/mongodb.conf
root     26316 26126  0 16:47 pts/0    00:00:00 grep mongo
[root@slave-node ~]# lsof -i:27017
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mongod  26290 root    7u  IPv4 663904      0t0  TCP slave-node1:27017 (LISTEN)
mongod  26290 root   25u  IPv4 663917      0t0  TCP slave-node1:51060->slave-node2:27017 (ESTABLISHED)

在slave-node測試連線master-node的mongodb資料庫是否正常
[root@slave-node ~]# mongo 182.48.115.236:27017
MongoDB shell version v3.4.4
connecting to: 182.48.115.236:27017
MongoDB server version: 3.4.4
Server has startup warnings: 
2017-06-03T16:47:31.200+0800 I STORAGE  [initandlisten] 
2017-06-03T16:47:31.200+0800 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2017-06-03T16:47:31.200+0800 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
2017-06-03T16:47:32.472+0800 I CONTROL  [initandlisten] 
2017-06-03T16:47:32.472+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-06-03T16:47:32.472+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-06-03T16:47:32.472+0800 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2017-06-03T16:47:32.472+0800 I CONTROL  [initandlisten] 
2017-06-03T16:47:32.473+0800 I CONTROL  [initandlisten] 
2017-06-03T16:47:32.473+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2017-06-03T16:47:32.473+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-06-03T16:47:32.473+0800 I CONTROL  [initandlisten] 
2017-06-03T16:47:32.473+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-06-03T16:47:32.473+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-06-03T16:47:32.473+0800 I CONTROL  [initandlisten] 
>

3)主從資料同步測試
在master-node節點資料庫裡建立master_slave庫,並插入20條資料
[root@master-node ~]# mongo 182.48.115.238:27017
MongoDB shell version v3.4.4
connecting to: 182.48.115.238:27017
MongoDB server version: 3.4.4
Server has startup warnings: 
2017-06-03T16:45:07.406+0800 I STORAGE  [initandlisten] 
2017-06-03T16:45:07.407+0800 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2017-06-03T16:45:07.407+0800 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
2017-06-03T16:45:08.373+0800 I CONTROL  [initandlisten] 
2017-06-03T16:45:08.373+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-06-03T16:45:08.373+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-06-03T16:45:08.373+0800 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2017-06-03T16:45:08.373+0800 I CONTROL  [initandlisten] 
2017-06-03T16:45:08.374+0800 I CONTROL  [initandlisten] 
2017-06-03T16:45:08.374+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2017-06-03T16:45:08.374+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-06-03T16:45:08.374+0800 I CONTROL  [initandlisten] 
2017-06-03T16:45:08.374+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-06-03T16:45:08.374+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-06-03T16:45:08.374+0800 I CONTROL  [initandlisten] 
> use master_slave
switched to db master_slave
> function add(){var i = 0;for(;i<20;i++){db.persons.insert({"name":"wang"+i})}}
> add()
> db.persons.find()
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbb"), "name" : "wang0" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbc"), "name" : "wang1" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbd"), "name" : "wang2" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbe"), "name" : "wang3" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbf"), "name" : "wang4" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc0"), "name" : "wang5" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc1"), "name" : "wang6" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc2"), "name" : "wang7" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc3"), "name" : "wang8" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc4"), "name" : "wang9" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc5"), "name" : "wang10" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc6"), "name" : "wang11" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc7"), "name" : "wang12" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc8"), "name" : "wang13" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc9"), "name" : "wang14" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dca"), "name" : "wang15" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dcb"), "name" : "wang16" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dcc"), "name" : "wang17" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dcd"), "name" : "wang18" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dce"), "name" : "wang19" }
Type "it" for more

然後在slave-node節點資料庫裡檢視,是否將master-node寫入的新資料同步過來了
[root@slave-node log]# mongo 182.48.115.236:27017
MongoDB shell version v3.4.4
connecting to: 182.48.115.236:27017
MongoDB server version: 3.4.4
Server has startup warnings: 
2017-06-03T16:56:28.928+0800 I STORAGE  [initandlisten] 
2017-06-03T16:56:28.928+0800 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2017-06-03T16:56:28.928+0800 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
2017-06-03T16:56:29.883+0800 I CONTROL  [initandlisten] 
2017-06-03T16:56:29.883+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-06-03T16:56:29.883+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-06-03T16:56:29.883+0800 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2017-06-03T16:56:29.883+0800 I CONTROL  [initandlisten] 
2017-06-03T16:56:29.884+0800 I CONTROL  [initandlisten] 
2017-06-03T16:56:29.884+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2017-06-03T16:56:29.884+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-06-03T16:56:29.884+0800 I CONTROL  [initandlisten] 
2017-06-03T16:56:29.884+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-06-03T16:56:29.884+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-06-03T16:56:29.884+0800 I CONTROL  [initandlisten] 
> show dbs
2017-06-03T17:10:32.136+0800 E QUERY    [thread1] Error: listDatabases failed:{
    "ok" : 0,
    "errmsg" : "not master and slaveOk=false",
    "code" : 13435,
    "codeName" : "NotMasterNoSlaveOk"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1
shellHelper.show@src/mongo/shell/utils.js:769:19
shellHelper@src/mongo/shell/utils.js:659:15
@(shellhelp2):1:1
> rs.slaveOk();
> show dbs
admin         0.000GB
local         0.000GB
master_slave  0.000GB
wangshibo     0.000GB
> use master_slave
switched to db master_slave
> db.persons.find()
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbb"), "name" : "wang0" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbc"), "name" : "wang1" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbd"), "name" : "wang2" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbe"), "name" : "wang3" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dbf"), "name" : "wang4" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc0"), "name" : "wang5" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc1"), "name" : "wang6" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc2"), "name" : "wang7" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc3"), "name" : "wang8" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc4"), "name" : "wang9" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc5"), "name" : "wang10" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc6"), "name" : "wang11" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc7"), "name" : "wang12" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc8"), "name" : "wang13" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dc9"), "name" : "wang14" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dca"), "name" : "wang15" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dcb"), "name" : "wang16" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dcc"), "name" : "wang17" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dcd"), "name" : "wang18" }
{ "_id" : ObjectId("593278699a9e2e9f37ac4dce"), "name" : "wang19" }
Type "it" for more
........................................................................................
如果在slave-node節點上的資料庫中檢視,有報錯:"errmsg" : "not master and slaveOk=false"!!!
首先這是正常的,因為SECONDARY是不允許讀寫的, 在寫多讀少的應用中,使用Replica Sets來實現讀寫分離。
通過在連線時指定或者在主庫指定slaveOk,由Secondary來分擔讀的壓力,Primary只承擔寫操作。
對於replica set 中的secondary 節點預設是不可讀的。

解決辦法:
在slave-node節點資料庫中執行"rs.slaveOk();"命令即可
........................................................................................

在slave-node節點資料庫中發現已經同步過來了master_slave庫的20條資料,說明mongodb的主從複製環境已經成功了!當配置完主從伺服器後,一但主伺服器上的資料發生變化,從伺服器也會發生變化

主從複製的原理

在主從結構中,主節點的操作記錄成為oplog(operation log)。oplog儲存在一個系統資料庫local的集合oplog.$main中,這個集合的每個文件都代表主節點上執行的一個操作。 
從伺服器會定期從主伺服器中獲取oplog記錄,然後在本機上執行!對於儲存oplog的集合,MongoDB採用的是固定集合,也就是說隨著操作過多,新的操作會覆蓋舊的操作!

主從複製的其他設定項
--only             從節點指定複製某個資料庫,預設是複製全部資料庫 
--slavedelay       從節點設定主資料庫同步資料的延遲(單位是秒) 
--fastsync         從節點以主資料庫的節點快照為節點啟動從資料庫 
--autoresync       從節點如果不同步則從新同步資料庫(即選擇當通過熱新增了一臺從伺服器之後,從伺服器選擇是否更新主伺服器之間的資料) 
--oplogSize        主節點設定oplog的大小(主節點操作記錄儲存到local的oplog中)

在上面slave-node從節點的local資料庫中,存在一個集合sources。這個集合就儲存了這個伺服器的主伺服器是誰

[root@slave-node mongodb]# mongo 182.48.115.236:27017
......
> show dbs
admin         0.000GB
local         0.000GB
master_slave  0.000GB
wangshibo     0.000GB
> use local
switched to db local
> show collections
me
sources
startup_log
> db.sources.find()
{ "_id" : ObjectId("593277a5105051e5648605a3"), "host" : "182.48.115.238:27017", "source" : "main", "syncedTo" : Timestamp(1496481652, 1) }
> 

二、Mongodb副本集(Replica Sets)   (可以參考:搭建高可用mongodb叢集

mongodb 不推薦主從複製,推薦建立副本集(Replica Set)來保證1個服務掛了,可以有其他服務頂上,程式正常執行,幾個服務的資料都是一樣的,後臺自動同步。主從複製其實就是一個單副本的應用,沒有很好的擴充套件性餓容錯性。然而副本集具有多個副本保證了容錯性,就算一個副本掛掉了還有很多個副本存在,並且解決了"主節點掛掉後,整個叢集內會自動切換"的問題。副本集比傳統的Master-Slave主從複製有改進的地方就是它可以進行故障的自動轉移,如果我們停掉複製集中的一個成員,那麼剩餘成員會再自動選舉一個成員,作為主庫。
Replica Set 使用的是 n 個 mongod 節點,構建具備自動的容錯功能(auto-failover),自動恢復的(auto-recovery)的高可用方案。使用 Replica Set 來實現讀寫分離。通過在連線時指定或者在主庫指定 slaveOk,由Secondary 來分擔讀的壓力,Primary 只承擔寫操作。對於 Replica Set 中的 secondary 節點預設是不可讀的。

1)關於副本集的概念

副本集是一種在多臺機器同步資料的程式,副本集體提供了資料冗餘,擴充套件了資料可用性。在多臺伺服器儲存資料可以避免因為一臺伺服器導致的資料丟失。
也可以從硬體故障或服務中斷解脫出來,利用額外的資料副本,可以從一臺機器致力於災難恢復或者備份。

在一些場景,可以使用副本集來擴充套件讀效能,客戶端有能力傳送讀寫操作給不同的伺服器。也可以在不同的資料中心獲取不同的副本來擴充套件分散式應用的能力。
mongodb副本集是一組擁有相同資料的mongodb例項,主mongodb接受所有的寫操作,所有的其他例項可以接受主例項的操作以保持資料同步。
主例項接受客戶的寫操作,副本集只能有一個主例項,因為為了維持資料一致性,只有一個例項可寫,主例項的日誌儲存在oplog。

Client Application Driver
  Writes  Reads
    |   |
    Primary
  |Replication|Replication
Secondary    Secondary

二級節點複製主節點的oplog然後在自己的資料副本上執行操作,二級節點是主節點資料的反射,如果主節點不可用,會選舉一個新的主節點。
預設讀操作是在主節點進行的,但是可以指定讀取首選項引數來指定讀操作到副本節點。
可以新增一個額外的仲裁節點(不擁有被選舉權),使副本集節點保持奇數,確保可以選舉出票數不同的直接點。仲裁者並不需要專用的硬體裝置。
仲裁者節點一直會儲存仲裁者身份

........非同步複製........
副本節點同步直接點操作是非同步的,然而會導致副本集無法返回最新的資料給客戶端程式。

........自動故障轉移........
如果主節點10s以上與其他節點失去通訊,其他節點將會選舉新的節點作為主節點。
擁有大多數選票的副節點會被選舉為主節點。
副本集提供了一些選項給應用程式,可以做一個成員位於不同資料中心的副本集。
也可以指定成員不同的優先順序來控制選舉。

2)副本集的結構及原理

MongoDB 的副本集不同於以往的主從模式。
在叢集Master故障的時候,副本集可以自動投票,選舉出新的Master,並引導其餘的Slave伺服器連線新的Master,而這個過程對於應用是透明的。可以說MongoDB的副本集
是自帶故障轉移功能的主從複製。
相對於傳統主從模式的優勢
傳統的主從模式,需要手工指定叢集中的 Master。如果 Master 發生故障,一般都是人工介入,指定新的 Master。 這個過程對於應用一般不是透明的,往往伴隨著應用重
新修改配置檔案,重啟應用伺服器等。而 MongoDB 副本集,叢集中的任何節點都可能成為 Master 節點。一旦 Master 節點故障,則會在其餘節點中選舉出一個新的 Master 節點。 並引導剩餘節點連線到新的 Master 節點。這個過程對於應用是透明的。

一個副本集即為服務於同一資料集的多個 MongoDB 例項,其中一個為主節點,其餘的都為從節點。主節 點上能夠完成讀寫操作,從節點僅能用於讀操作。主節點需要記錄所有改變資料庫狀態的操作,這些記錄 儲存在 oplog 中,這個檔案儲存在 local 資料庫,各個從節點通過此 oplog 來複制資料並應用於本地,保持 本地的資料與主節點的一致。oplog 具有冪等性,即無論執行幾次其結果一致,這個比 mysql 的二進位制日 志更好用。
叢集中的各節點還會通過傳遞心跳資訊來檢測各自的健康狀況。當主節點故障時,多個從節點會觸發一次 新的選舉操作,並選舉其中的一個成為新的主節點(通常誰的優先順序更高,誰就是新的主節點),心跳信 息預設每 2 秒傳遞一次。

客戶端連線到副本集後,不關心具體哪一臺機器是否掛掉。主伺服器負責整個副本集的讀寫,副本集定期同步資料備份。一旦主節點掛掉,副本節點就會選舉一個新的主伺服器。這一切對於應用伺服器不需要關心。

心跳檢測:
整個叢集需要保持一定的通訊才能知道哪些節點活著哪些節點掛掉。mongodb節點會向副本集中的其他節點每兩秒就會傳送一次pings包,如果其他節點在10秒鐘
之內沒有返回就標示為不能訪問。每個節點內部都會維護一個狀態對映表,表明當前每個節點是什麼角色、日誌時間戳等關鍵資訊。如果是主節點,除了維護對映表
外還需要檢查自己能否和叢集中內大部分節點通訊,如果不能則把自己降級為secondary只讀節點。

資料同步
副本集同步分為初始化同步和keep複製。初始化同步指全量從主節點同步資料,如果主節點資料量比較大同步時間會比較長。而keep複製指初始化同步過後,節點
之間的實時同步一般是增量同步。初始化同步不只是在第一次才會被處罰,有以下兩種情況會觸發:
1)secondary第一次加入,這個是肯定的。
2)secondary落後的資料量超過了oplog的大小,這樣也會被全量複製。

副本集中的副本節點在主節點掛掉後通過心跳機制檢測到後,就會在叢集內發起主節點的選舉機制,自動選舉出一位新的主伺服器。

副本集包括三種節點:主節點、從節點、仲裁節點。

1)主節點負責處理客戶端請求,讀、寫資料, 記錄在其上所有操作的 oplog;
2)從節點定期輪詢主節點獲取這些操作,然後對自己的資料副本執行這些操作,從而保證從節點的資料與主節點一致。預設情況下,從節點不支援外部讀取,但可以設定;
   副本集的機制在於主節點出現故障的時候,餘下的節點會選舉出一個新的主節點,從而保證系統可以正常執行。
3)仲裁節點不復制資料,僅參與投票。由於它沒有訪問的壓力,比較空閒,因此不容易出故障。由於副本集出現故障的時候,存活的節點必須大於副本集節點總數的一半,
   否則無法選舉主節點,或者主節點會自動降級為從節點,整個副本集變為只讀。因此,增加一個不容易出故障的仲裁節點,可以增加有效選票,降低整個副本集不可用的
   風險。仲裁節點可多於一個。也就是說只參與投票,不接收復制的資料,也不能成為活躍節點。

官方推薦MongoDB副本節點最少為3臺, 建議副本整合員為奇數,最多12個副本節點,最多7個節點參與選舉。限制副本節點的數量,主要是因為一個叢集中過多的副本節點,增加了複製的成本,反而拖累了叢集
的整體效能。 太多的副本節點參與選舉,也會增加選舉的時間。而官方建議奇數的節點,是為了避免腦裂 的發生。

3)副本集的工作流程 

在 MongoDB 副本集中,主節點負責處理客戶端的讀寫請求,備份節點則負責對映主節點的 資料。備份節點的工作原理過程可以大致描述為,備份節點定期輪詢主節點上的資料操作,
然後對 自己的資料副本進行這些操作,從而保證跟主節點的資料同步。至於主節點上的所有 資料庫狀態改變 的操作,都會存放在一張特定的系統表中。備份節點則是根據這些資料進
行自己的資料更新。

oplog
上面提到的資料庫狀態改變的操作,稱為 oplog(operation log,主節點操作記錄)。oplog 儲存在 local 資料庫的"oplog.rs"表中。副本集中備份節點非同步的從主節點同步 oplog,然後重新 執行它記錄的操作,以此達到了資料同步的作用。
關於 oplog 有幾個注意的地方:
1)oplog 只記錄改變資料庫狀態的操作
2)儲存在 oplog 中的操作並不是和主節點執行的操作完全一樣,例如"$inc"操作就會轉化為"$set"操作
3)oplog 儲存在固定集合中(capped collection),當 oplog 的數量超過 oplogSize,新的操作就會覆蓋就的操作

資料同步
在副本集中,有兩種資料同步方式:
1)initial sync(初始化):這個過程發生在當副本集中建立一個新的資料庫或其中某個節點剛從當機中恢復,或者向副本集中新增新的成員的時候,預設的,副本集中的節點會從離 它最近
   的節點複製 oplog 來同步資料,這個最近的節點可以是 primary 也可以是擁有最新 oplog 副本的 secondary 節點。該操作一般會重新初始化備份節點,開銷較大。
2)replication(複製):在初始化後這個操作會一直持續的進行著,以保持各個 secondary 節點之間的資料同步。

initial sync
當遇到無法同步的問題時,只能使用以下兩種方式進行 initial sync 了
1)第一種方式就是停止該節點,然後刪除目錄中的檔案,重新啟動該節點。這樣,這個節 點就會執行 initial sync
   注意:通過這種方式,sync 的時間是根據資料量大小的,如果資料量過大,sync 時間就 會很長
   同時會有很多網路傳輸,可能會影響其他節點的工作
2)第二種方式,停止該節點,然後刪除目錄中的檔案,找一個比較新的節點,然後把該節點目 錄中的檔案拷貝到要 sync 的節點目錄中
通過上面兩種方式中的一種,都可以重新恢復"port=33333"的節點。不在進行截圖了。

副本集管理
1)檢視oplog的資訊 通過"db.printReplicationInfo()"命令可以檢視 oplog 的資訊
   欄位說明:
   configured oplog size: oplog 檔案大小
   log length start to end:     oplog 日誌的啟用時間段
   oplog first event time:      第一個事務日誌的產生時間
   oplog last event time:       最後一個事務日誌的產生時間
   now:                         現在的時間

2)檢視 slave 狀態 通過"db.printSlaveReplicationInfo()"可以檢視 slave 的同步狀態
  當插入一條新的資料,然後重新檢查 slave 狀態時,就會發現 sync 時間更新了

4)副本集選舉的過程和注意點

Mongodb副本集選舉採用的是Bully演算法,這是一種協調者(主節點)競選演算法,主要思想是叢集的每個成員都可以宣告它是主節點並通知其他節點。
別的節點可以選擇接受這個聲稱或是拒絕並進入主節點競爭,被其他所有節點接受的節點才能成為主節點。 
節點按照一些屬性來判斷誰應該勝出,這個屬性可以是一個靜態 ID,也可以是更新的度量像最近一次事務ID(最新的節點會勝出) 

副本集的選舉過程大致如下:
1)得到每個伺服器節點的最後操作時間戳。每個 mongodb 都有 oplog 機制會記錄本機的操作,方便和主服 務器進行對比資料是否同步還可以用於錯誤恢復。
2)如果叢集中大部分伺服器 down 機了,保留活著的節點都為 secondary 狀態並停止,不選舉了。
3)如果叢集中選舉出來的主節點或者所有從節點最後一次同步時間看起來很舊了,停止選舉等待人來操作。
4)如果上面都沒有問題就選擇最後操作時間戳最新(保證資料是最新的)的伺服器節點作為主節點。


副本集選舉的特點:
選舉還有個前提條件,參與選舉的節點數量必須大於副本集總節點數量的一半(建議副本整合員為奇數。最多12個副本節點,最多7個節點參與選舉)
如果已經小於一半了所有節點保持只讀狀態。集合中的成員一定要有大部分成員(即超過一半數量)是保持正常線上狀態,3個成員的副本集,需要至少2個從屬節點是正常狀態。
如果一個從屬節點掛掉,那麼當主節點down掉 產生故障切換時,由於副本集中只有一個節點是正常的,少於一半,則選舉失敗。
4個成員的副本集,則需要3個成員是正常狀態(先關閉一個從屬節點,然後再關閉主節點,產生故障切換,此時副本集中只有2個節點正常,則無法成功選舉出新主節點)。

5)副本集資料過程

Primary節點寫入資料,Secondary通過讀取Primary的oplog得到複製資訊,開始複製資料並且將複製資訊寫入到自己的oplog。如果某個操作失敗,則備份節點
停止從當前資料來源複製資料。如果某個備份節點由於某些原因掛掉了,當重新啟動後,就會自動從oplog的最後一個操作開始同步,同步完成後,將資訊寫入自己的
oplog,由於複製操作是先複製資料,複製完成後再寫入oplog,有可能相同的操作會同步兩份,不過MongoDB在設計之初就考慮到這個問題,將oplog的同一個操作
執行多次,與執行一次的效果是一樣的。簡單的說就是:

當Primary節點完成資料操作後,Secondary會做出一系列的動作保證資料的同步:
1)檢查自己local庫的oplog.rs集合找出最近的時間戳。
2)檢查Primary節點local庫oplog.rs集合,找出大於此時間戳的記錄。
3)將找到的記錄插入到自己的oplog.rs集合中,並執行這些操作。

副本集的同步和主從同步一樣,都是非同步同步的過程,不同的是副本集有個自動故障轉移的功能。其原理是:slave端從primary端獲取日誌,然後在自己身上完全順序
的執行日誌所記錄的各種操作(該日誌是不記錄查詢操作的),這個日誌就是local資料 庫中的oplog.rs表,預設在64位機器上這個表是比較大的,佔磁碟大小的5%,
oplog.rs的大小可以在啟動引數中設 定:--oplogSize 1000,單位是M。

注意:在副本集的環境中,要是所有的Secondary都當機了,只剩下Primary。最後Primary會變成Secondary,不能提供服務。

6)MongoDB 同步延遲問題

當你的使用者抱怨修改過的資訊不改變,刪除掉的資料還在顯示,你掐指一算,估計是資料庫主從不同步。與其他提供資料同步的資料庫一樣,MongoDB 也會遇到同步延遲的問題,
在MongoDB的Replica Sets模式中,同步延遲也經常是困擾使用者的一個大問題。

什麼是同步延遲?
首先,要出現同步延遲,必然是在有資料同步的場合,在 MongoDB 中,有兩種資料冗餘方式,一種是Master-Slave 模式,一種是Replica Sets模式。這兩個模式本質上都是
在一個節點上執行寫操作, 另外的節點將主節點上的寫操作同步到自己這邊再進行執行。在MongoDB中,所有寫操作都會產生 oplog,oplog 是每修改一條資料都會生成一條,如果你採用一個批量 update 命令更新了 N 多條資料, 那麼抱歉,oplog 會有很多條,而不是一條。所以同步延遲就是寫操作在主節點上執行完後,從節點還沒有把 oplog 拿過來再執行一次。而這個寫操作的量越大,主節點與從節點的差別也就越大,同步延遲也就越大了。

同步延遲帶來的問題
首先,同步操作通常有兩個效果,一是讀寫分離,將讀操作放到從節點上來執行,從而減少主節點的 壓力。對於大多數場景來說,讀多寫少是基本特性,所以這一點是很有用的。
另一個作用是資料備份, 同一個寫操作除了在主節點執行之外,在從節點上也同樣執行,這樣我們就有多份同樣的資料,一旦 主節點的資料因為各種天災人禍無法恢復的時候,我們至少還有從節點可以依賴。但是主從延遲問題 可能會對上面兩個效果都產生不好的影響。

如果主從延遲過大,主節點上會有很多資料更改沒有同步到從節點上。這時候如果主節點故障,就有 兩種情況:
1)主節點故障並且無法恢復,如果應用上又無法忍受這部分資料的丟失,我們就得想各種辦法將這部 資料更改找回來,再寫入到從節點中去。可以想象,即使是有可能,那這也絕對是一件非常噁心的活。
2)主節點能夠恢復,但是需要花的時間比較長,這種情況如果應用能忍受,我們可以直接讓從節點提 供服務,只是對使用者來說,有一段時間的資料丟失了,而如果應用不能接受資料的不一致,那麼就只能下線整個業務,等主節點恢復後再提供服務了。

如果你只有一個從節點,當主從延遲過大時,由於主節點只儲存最近的一部分 oplog,可能會導致從 節點青黃不接,不得不進行 resync 操作,全量從主節點同步資料。
帶來的問題是:當從節點全量同步的時候,實際只有主節點儲存了完整的資料,這時候如果主節點故障,很可能全 部資料都丟掉了。

7)Mongodb副本集環境部署記錄

1)機器環境
182.48.115.236    master-node(主節點)
182.48.115.237    slave-node1(從節點)
182.48.115.238    slave-node2(從節點)

MongoDB 安裝目錄:/usr/local/mongodb
MongoDB 資料庫目錄:/usr/local/mongodb/data
MongoDB 日誌目錄:/usr/local/mongodb/log/mongo.log 
MongoDB 配置檔案:/usr/local/mongodb/mongodb.conf

mongodb安裝可以參考:http://www.cnblogs.com/kevingrace/p/5752382.html

對以上三臺伺服器部署Mongodb的副本集功能,定義副本集名稱為:hqmongodb
關閉三臺伺服器的iptables防火牆和selinux

2)確保三臺副本集伺服器上的配置檔案完全相同(即三臺機器的mongodb.conf配置一樣,除了配置檔案中繫結的ip不一樣)。下面操作在三臺節點機上都要執行:

編寫配置檔案
[root@master-node ~]# cat /usr/local/mongodb/mongodb.conf
port=27017
bind_ip = 182.48.115.236                 //這個最好配置成本機的ip地址。否則後面進行副本集初始化的時候可能會失敗!            
dbpath=/usr/local/mongodb/data
logpath=/usr/local/mongodb/log/mongo.log
pidfilepath=/usr/local/mongodb/mongo.pid
fork=true
logappend=true 
shardsvr=true 
directoryperdb=true
#auth=true
#keyFile =/usr/local/mongodb/keyfile
replSet =hqmongodb

編寫啟動指令碼(各個節點需要將指令碼中的ip改為本機自己的ip地址)
[root@master-node ~]# cat /usr/local/mongodb/mongodb.conf
port=27017
dbpath=/usr/local/mongodb/data
logpath=/usr/local/mongodb/log/mongo.log
pidfilepath=/usr/local/mongodb/mongo.pid
fork=true
logappend=true 
shardsvr=true 
directoryperdb=true
#auth=true
#keyFile =/usr/local/mongodb/keyfile
replSet =hqmongodb
[root@master-node ~]# cat /etc/init.d/mongodb
#!/bin/sh
# chkconfig: - 64 36
# description:mongod
case $1 in
start)
/usr/local/mongodb/bin/mongod --maxConns 20000 --config /usr/local/mongodb/mongodb.conf
;;
stop)
/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.shutdownServer()"
#/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.auth('system', '123456');db.shutdownServer()"
;;
status)
/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.stats()"
#/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.auth('system', '123456');db.stats()"
;; esac

啟動mongodb
[root@master-node ~]# ulimit -SHn 655350

[root@master-node ~]# /etc/init.d/mongodb start
about to fork child process, waiting until server is ready for connections.
forked process: 28211
child process started successfully, parent exiting
[root@master-node ~]# ps -ef|grep mongodb
root     28211     1  2 00:30 ?        00:00:00 /usr/local/mongodb/bin/mongod --maxConns 20000 --config /usr/local/mongodb/mongodb.conf
root     28237 27994  0 00:30 pts/2    00:00:00 grep mongodb

[root@master-node ~]# lsof -i:27017
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mongod  28211 root    7u  IPv4 684206      0t0  TCP *:27017 (LISTEN)
------------------------------------------------------------------
如果啟動mongodb的時候報錯如下:
about to fork child process, waiting until server is ready for connections.
forked process: 14229
ERROR: child process failed, exited with error number 100

這算是一個Mongod 啟動的一個常見錯誤,非法關閉的時候,lock 檔案沒有幹掉,第二次啟動的時候檢查到有lock 檔案的時候,就報這個錯誤了。
解決方法:進入mongod啟動時指定的data目錄下刪除lock檔案,然後執行"./mongod  --repair"就行了。即
# rm -rf /usr/local/mongodb/data/mongod.lock                   //由於我的測試環境下沒有資料,我將data資料目錄下的檔案全部清空,然後--repair
# /usr/local/mongodb/bin/mongod --repair

然後再啟動mongodb就ok了!
------------------------------------------------------------------

3)對master-node主節點進行配置(182.48.115.236)         //其實,剛開始這三個節點中的任何一個都可以用來初始化為開始的主節點。這裡選擇以master-node為主節點
[root@master-node ~]# mongo 182.48.115.236:27017        //登陸到mongodb資料庫中執行下面命令操作。由於配置檔案中繫結了ip,所以要用這個繫結的ip登陸
....

3.1)初始化副本集,設定本機為主節點 PRIMARY

> rs.initiate()
{
  "info2" : "no configuration specified. Using a default configuration for the set",
  "me" : "182.48.115.236:27017",
  "ok" : 1
}
hqmongodb:OTHER> rs.conf()
{
  "_id" : "hqmongodb",
  "version" : 1,
  "protocolVersion" : NumberLong(1),
  "members" : [
    {
      "_id" : 0,
      "host" : "182.48.115.236:27017",
      "arbiterOnly" : false,
      "buildIndexes" : true,
      "hidden" : false,
      "priority" : 1,
      "tags" : {
        
      },
      "slaveDelay" : NumberLong(0),
      "votes" : 1
    }
  ],
  "settings" : {
    "chainingAllowed" : true,
    "heartbeatIntervalMillis" : 2000,
    "heartbeatTimeoutSecs" : 10,
    "electionTimeoutMillis" : 10000,
    "catchUpTimeoutMillis" : 2000,
    "getLastErrorModes" : {
      
    },
    "getLastErrorDefaults" : {
      "w" : 1,
      "wtimeout" : 0
    },
    "replicaSetId" : ObjectId("5932f142a55dc83eca86ea86")
  }
}

3.2)新增副本集從節點。(發現在執行上面的兩個命令後,字首已經改成"hqmongodb:PRIMARY"了,即已經將其自己設定為主節點 PRIMARY了)
hqmongodb:PRIMARY> rs.add("182.48.115.237:27017")
{ "ok" : 1 }
hqmongodb:PRIMARY> rs.add("182.48.115.238:27017")
{ "ok" : 1 }

3.3)設定節點優先順序
hqmongodb:PRIMARY> cfg = rs.conf()          //檢視節點順序
{
  "_id" : "hqmongodb",
  "version" : 3,
  "protocolVersion" : NumberLong(1),
  "members" : [
    {
      "_id" : 0,
      "host" : "182.48.115.236:27017",
      "arbiterOnly" : false,
      "buildIndexes" : true,
      "hidden" : false,
      "priority" : 1,
      "tags" : {
        
      },
      "slaveDelay" : NumberLong(0),
      "votes" : 1
    },
    {
      "_id" : 1,
      "host" : "182.48.115.237:27017",
      "arbiterOnly" : false,
      "buildIndexes" : true,
      "hidden" : false,
      "priority" : 1,
      "tags" : {
        
      },
      "slaveDelay" : NumberLong(0),
      "votes" : 1
    },
    {
      "_id" : 2,
      "host" : "182.48.115.238:27017",
      "arbiterOnly" : false,
      "buildIndexes" : true,
      "hidden" : false,
      "priority" : 1,
      "tags" : {
        
      },
      "slaveDelay" : NumberLong(0),
      "votes" : 1
    }
  ],
  "settings" : {
    "chainingAllowed" : true,
    "heartbeatIntervalMillis" : 2000,
    "heartbeatTimeoutSecs" : 10,
    "electionTimeoutMillis" : 10000,
    "catchUpTimeoutMillis" : 2000,
    "getLastErrorModes" : {
      
    },
    "getLastErrorDefaults" : {
      "w" : 1,
      "wtimeout" : 0
    },
    "replicaSetId" : ObjectId("5932f142a55dc83eca86ea86")
  }
}

hqmongodb:PRIMARY> cfg.members[0].priority = 1
1
hqmongodb:PRIMARY> cfg.members[1].priority = 1
1
hqmongodb:PRIMARY> cfg.members[2].priority = 2      //設定_ID 為 2 的節點為主節點。即噹噹前主節點發生故障時,該節點就會轉變為主節點接管服務
2
hqmongodb:PRIMARY> rs.reconfig(cfg)                 //使配置生效
{ "ok" : 1 }


說明:
MongoDB副本集通過設定priority 決定優先順序,預設優先順序為1,priority值是0到100之間的數字,數字越大優先順序越高,priority=0,則此節點永遠不能成為主節點 primay。
cfg.members[0].priority =1 引數,中括號裡的數字是執行rs.conf()檢視到的節點順序, 第一個節點是0,第二個節點是 1,第三個節點是 2,以此類推。優先順序最高的那個
被設定為主節點。

4)分別對兩臺從節點進行配置

slave-node1節點操作(182.48.115.237)
[root@slave-node1 ~]# mongo 182.48.115.237:27017
.....
hqmongodb:SECONDARY> db.getMongo().setSlaveOk()        //設定從節點為只讀.注意從節點的字首現在是SECONDARY。看清楚才設定

slave-node2節點操作(182.48.115.238)
[root@slave-node2 ~]# mongo 182.48.115.238:27017
......
hqmongodb:SECONDARY> db.getMongo().setSlaveOk()       //從節點的字首是SECONDARY,看清楚才設定。執行這個,否則後續從節點同步資料時會報錯:"errmsg" : "not master and slaveOk=false",

5)設定資料庫賬號,開啟登入驗證(這一步可以直接跳過,即不開啟登陸驗證,只是為了安全著想)
5.1)設定資料庫賬號
在master-node主節點伺服器182.48.115.236上面操作
[root@master-node ]# mongo 182.48.115.236:27017
......
-------------------------------------------------
如果執行命令後出現報錯: "errmsg" : "not master and slaveOk=false",
這是正常的,因為SECONDARY是不允許讀寫的,如果非要解決,方法如下:
> rs.slaveOk();              //執行這個命令然後,再執行其它命令就不會出現這個報錯了
-------------------------------------------------
hqmongodb:PRIMARY> show dbs
local  0.000GB        
hqmongodb:PRIMARY> use admin     //mongodb3.0沒有admin資料庫了,需要手動建立。admin庫下新增的賬號才是管理員賬號
switched to db admin    
hqmongodb:PRIMARY> show collections

#新增兩個管理員賬號,一個系統管理員:system 一個資料庫管理員:administrator
#先新增系統管理員賬號,用來管理使用者
hqmongodb:PRIMARY> db.createUser({user:"system",pwd:"123456",roles:[{role:"root",db:"admin"}]})
Successfully added user: {
  "user" : "system",
  "roles" : [
    {
      "role" : "root",
      "db" : "admin"
    }
  ]
}
hqmongodb:PRIMARY> db.auth('system','123456')        //新增管理員使用者認證,認證之後才能管理所有資料庫
1

#新增資料庫管理員,用來管理所有資料庫
hqmongodb:PRIMARY> db.createUser({user:'administrator', pwd:'123456', roles:[{ role: "userAdminAnyDatabase", db: "admin"}]});
Successfully added user: {
  "user" : "administrator",
  "roles" : [
    {
      "role" : "userAdminAnyDatabase",
      "db" : "admin"
    }
  ]
}
hqmongodb:PRIMARY> db.auth('administrator','123456')      //新增管理員使用者認證,認證之後才能管理所有資料庫
1

hqmongodb:PRIMARY> db
admin
hqmongodb:PRIMARY> show collections
system.users
system.version
hqmongodb:PRIMARY> db.system.users.find()
{ "_id" : "admin.system", "user" : "system", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "uTGH9NI6fVUFXd2u7vu3Pw==", "storedKey" : "qJBR7dlqj3IgnWpVbbqBsqo6ECs=", "serverKey" : "pTQhfZohNh760BED7Zn1Vbety4k=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
{ "_id" : "admin.administrator", "user" : "administrator", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "zJ3IIgYCe4IjZm0twWnK2Q==", "storedKey" : "2UCFc7KK1k5e4BgWbkTKGeuOVB4=", "serverKey" : "eYHK/pBpf8ntrER1A8fiI+GikBY=" } }, "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }

退出,用剛才建立的賬號進行登入
[root@master-node ~]# mongo 182.48.115.236:27017 -u system -p 123456 --authenticationDatabase admin
[root@master-node ~]# mongo 182.48.115.236:27017 -u administrator -p 123456  --authenticationDatabase admin

5.2)開啟登入驗證
在master-node主節點伺服器182.48.115.236上面操作
[root@master-node ~]# cd /usr/local/mongodb/                        //切換到mongodb主目錄
[root@master-node mongodb]# openssl rand -base64 21 > keyfile      //建立一個 keyfile(使用 openssl 生成 21 位 base64 加密的字串)
[root@master-node mongodb]# chmod 600 /usr/local/mongodb/keyfile
[root@master-node mongodb]# cat /usr/local/mongodb/keyfile          //檢視剛才生成的字串,做記錄,後面要用到
RavtXslz/WTDwwW2JiNvK4OBVKxU

注意:上面的數字 21,最好是 3 的倍數,否則生成的字串可能含有非法字元,認證失敗。

5.3)設定配置檔案
分別在所有節點上面操作(即三個節點的配置檔案上都要修改)
[root@master-node ~]# vim /usr/local/mongodb/mongodb.conf     //新增下面兩行內容
......
auth=true
keyFile =/usr/local/mongodb/keyfile

啟動指令碼使用下面的程式碼(註釋原來的,啟用之前註釋掉的)
[root@master-node ~]# cat /etc/init.d/mongodb
#!/bin/sh
# chkconfig: - 64 36
# description:mongod
case $1 in
start)
/usr/local/mongodb/bin/mongod --maxConns 20000 --config /usr/local/mongodb/mongodb.conf
;;
stop)
#/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.shutdownServer()"
/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.auth('system', '123456');db.shutdownServer()"
;;
status)
#/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.stats()"
/usr/local/mongodb/bin/mongo 182.48.115.236:27017/admin --eval "db.auth('system', '123456');db.stats()"
;; esac

5.4)設定許可權驗證檔案
先進入master-node主節點伺服器182.48.115.236,檢視 
[root@master-node ~]# cat /usr/local/mongodb/keyfile
RavtXslz/WTDwwW2JiNvK4OBVKxU                              //檢視剛才生成的字串,做記錄

再分別進入兩臺從節點伺服器182.48.115.237/238
[root@slave-node1 ~]# vim /usr/local/mongodb/keyfile       //將主節點生成的許可權驗證字元碼複製到從節點的許可權驗證檔案裡
RavtXslz/WTDwwW2JiNvK4OBVKxU
[root@slave-node1 ~]# chmod 600 /usr/local/mongodb/keyfile

[root@slave-node2 ~]# vim /usr/local/mongodb/keyfile 
[root@slave-node2 ~]# cat /usr/local/mongodb/keyfile
RavtXslz/WTDwwW2JiNvK4OBVKxU
[root@slave-node2 ~]# chmod 600 /usr/local/mongodb/keyfile

6)驗證副本集
分別重啟三臺副本集伺服器(三臺節點都要重啟)
[root@master-node ~]# ps -ef|grep mongodb
root     28964     1  1 02:22 ?        00:00:31 /usr/local/mongodb/bin/mongod --maxConns 20000 --config /usr/local/mongodb/mongodb.conf
root     29453 28911  0 03:04 pts/0    00:00:00 grep mongodb
[root@master-node ~]# kill -9 28964
[root@master-node ~]# /etc/init.d/mongodb start
about to fork child process, waiting until server is ready for connections.
forked process: 29457
child process started successfully, parent exiting
[root@master-node ~]# lsof -i:27017
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mongod  29457 root    7u  IPv4 701471      0t0  TCP slave-node1:27017 (LISTEN)
mongod  29457 root   29u  IPv4 701526      0t0  TCP slave-node1:27017->master-node:39819 (ESTABLISHED)
mongod  29457 root   30u  IPv4 701573      0t0  TCP slave-node1:27017->master-node:39837 (ESTABLISHED)
mongod  29457 root   31u  IPv4 701530      0t0  TCP slave-node1:36768->master-node:27017 (ESTABLISHED)
mongod  29457 root   32u  IPv4 701549      0t0  TCP slave-node1:36786->master-node:27017 (ESTABLISHED)
mongod  29457 root   33u  IPv4 701574      0t0  TCP slave-node1:27017->master-node:39838 (ESTABLISHED)

然後登陸mongodb
[root@master-node ~]# mongo 182.48.115.236:27017 -u system -p 123456 --authenticationDatabase admin
.......
hqmongodb:PRIMARY> rs.status()             //副本集狀態檢視.也可以省略上面新增登陸驗證的步驟,不做驗證,直接檢視叢集狀態。叢集狀態中可以看出哪個節點目前是主節點
{
  "set" : "hqmongodb",
  "date" : ISODate("2017-06-03T19:06:59.708Z"),
  "myState" : 1,
  "term" : NumberLong(2),
  "heartbeatIntervalMillis" : NumberLong(2000),
  "optimes" : {
    "lastCommittedOpTime" : {
      "ts" : Timestamp(1496516810, 1),
      "t" : NumberLong(2)
    },
    "appliedOpTime" : {
      "ts" : Timestamp(1496516810, 1),
      "t" : NumberLong(2)
    },
    "durableOpTime" : {
      "ts" : Timestamp(1496516810, 1),
      "t" : NumberLong(2)
    }
  },
  "members" : [
    {
      "_id" : 0,
      "name" : "182.48.115.236:27017",
      "health" : 1,
      "state" : 1,                         //state的值為1的節點就是主節點
      "stateStr" : "PRIMARY",              //主節點PRIMARY標記
      "uptime" : 138,
      "optime" : {
        "ts" : Timestamp(1496516810, 1),
        "t" : NumberLong(2)
      },
      "optimeDate" : ISODate("2017-06-03T19:06:50Z"),
      "infoMessage" : "could not find member to sync from",
      "electionTime" : Timestamp(1496516709, 1),
      "electionDate" : ISODate("2017-06-03T19:05:09Z"),
      "configVersion" : 4,
      "self" : true
    },
    {
      "_id" : 1,
      "name" : "182.48.115.237:27017",
      "health" : 1,
      "state" : 2,
      "stateStr" : "SECONDARY",
      "uptime" : 116,
      "optime" : {
        "ts" : Timestamp(1496516810, 1),
        "t" : NumberLong(2)
      },
      "optimeDurable" : {
        "ts" : Timestamp(1496516810, 1),
        "t" : NumberLong(2)
      },
      "optimeDate" : ISODate("2017-06-03T19:06:50Z"),
      "optimeDurableDate" : ISODate("2017-06-03T19:06:50Z"),
      "lastHeartbeat" : ISODate("2017-06-03T19:06:59.533Z"),
      "lastHeartbeatRecv" : ISODate("2017-06-03T19:06:59.013Z"),
      "pingMs" : NumberLong(0),
      "syncingTo" : "182.48.115.236:27017",
      "configVersion" : 4
    },
    {
      "_id" : 2,
      "name" : "182.48.115.238:27017",
      "health" : 1,
      "state" : 2,
      "stateStr" : "SECONDARY",
      "uptime" : 189,
      "optime" : {
        "ts" : Timestamp(1496516810, 1),
        "t" : NumberLong(2)
      },
      "optimeDurable" : {
        "ts" : Timestamp(1496516810, 1),
        "t" : NumberLong(2)
      },
      "optimeDate" : ISODate("2017-06-03T19:06:50Z"),
      "optimeDurableDate" : ISODate("2017-06-03T19:06:50Z"),
      "lastHeartbeat" : ISODate("2017-06-03T19:06:59.533Z"),
      "lastHeartbeatRecv" : ISODate("2017-06-03T19:06:59.013Z"),
      "pingMs" : NumberLong(0),
      "syncingTo" : "182.48.115.236:27017",
      "configVersion" : 4
    },
  ],
  "ok" : 1
}

注意上面命令結果中的state,如果這個值為 1,說明是主控節點(master);如果是2,說明是從屬節點slave。在上面顯示的當前主節點寫入資料,到從節點上檢視發現資料會同步。
 
當主節點出現故障的時候,在兩個從節點上會選舉出一個新的主節點,故障恢復之後,之前的主節點會變為從節點。從上面叢集狀態中開看出,當前主節點是master-node,那麼關閉它的mongodb,再次檢視叢集狀態,就會發現主節點變為之前設定的slave-node2,即182.48.115.238了!
至此,Linux 下 MongoDB 副本集部署完成。

-------------------------------------------------------------------------------------------
新增資料,來需要驗證的--------------------
1)主從伺服器資料是否同步,從伺服器沒有讀寫許可權
a:向主伺服器寫入資料 ok 後臺自動同步到從伺服器,從伺服器有資料
b:向從伺服器寫入資料 false 從伺服器不能寫
c:主伺服器讀取資料 ok
d:從伺服器讀取資料 false 從伺服器不能讀

2)關閉主伺服器,從伺服器是否能頂替
 mongo 的命令列執行 rs.status() 發現 PRIMARY 替換了主機了

3)關閉的伺服器,再恢復,以及主從切換
 a:直接啟動關閉的服務,rs.status()中會發現,原來掛掉的主伺服器重啟後變成從伺服器了
 b:額外刪除新的伺服器 rs.remove("localhost:9933"); rs.status()
 c:額外增加新的伺服器 rs.add({_id:0,host:"localhost:9933",priority:1});
 d:讓新增的成為主伺服器 rs.stepDown(),注意之前的 priority 投票

4)從伺服器讀寫
 db.getMongo().setSlaveOk();
 db.getMongo().slaveOk();//從庫只讀,沒有寫許可權,這個方法 java 裡面不推薦了
 db.setReadPreference(ReadPreference.secondaryPreferred());// 在 復 制 集 中 優 先 讀
 secondary,如果 secondary 訪問不了的時候就從 master 中讀
 db.setReadPreference(ReadPreference.secondary());// 只 從 secondary 中 讀 , 如 果
 secondary 訪問不了的時候就不能進行查詢

日誌檢視--------------------------- 
 MongoDB 的 Replica Set 架構是通過一個日誌來儲存寫操作的,這個日誌就叫做”oplog”,
 它存在於”local”資料庫中,oplog 的大小是可以通過 mongod 的引數”—oplogSize”來改變
 oplog 的日誌大小。
 > use local
 switched to db local
 > db.oplog.rs.find()
 { "ts" : { "t" : 1342511269000, "i" : 1 }, "h" : NumberLong(0), "op" : "n", "ns" : "", "o" :
 { "msg" : "initiating set" } }
 
 欄位說明:
 ts: 某個操作的時間戳
 op: 操作型別,如下:
 i: insert
 d: delete
 u: update
 ns: 名稱空間,也就是操作的 collection name
-------------------------------------------------------------------------------------------
其它注意細節:
rs.remove("ip:port");       //刪除副本
單伺服器,啟動時新增--auth         引數開啟驗證。

副本集伺服器,開啟--auth 引數的同時,必須指定 keyfile 引數,節點之間的通訊基於該 keyfile,key 長度必須在 6 到 1024 個字元之間,
最好為 3 的倍數,不能含有非法字元。

重新設定副本集 
rs.stepDown() 
cfg = rs.conf()
cfg.members[n].host= 'new_host_name:prot'
rs.reconfig(cfg)

副本集所有節點伺服器總數必須為奇數,伺服器數量為偶數的時候,需要新增一個仲裁節 點,仲裁節點不參與數副本集,只有選舉權。

rs.addArb("182.48.115.239:27017") #新增仲裁節點

三、Mongodb分片叢集(Sharding)

Sharding cluster是一種可以水平擴充套件的模式,在資料量很大時特給力,實際大規模應用一般會採用這種架構去構建。sharding分片很好的解決了單臺伺服器磁碟空間、記憶體、cpu等硬體資源的限制問題,把資料水平拆分出去,降低單節點的訪問壓力。每個分片都是一個獨立的資料庫,所有的分片組合起來構成一個邏輯上的完整的資料庫。因此,分片機制降低了每個分片的資料操作量及需要儲存的資料量,達到多臺伺服器來應對不斷增加的負載和資料的效果

1)Sharding分割槽概念

分片 (sharding)是指將資料庫拆分,將其分散在不同的機器上的過程。將資料分散到不同的機器上,不需要功能強大的伺服器就可以儲存更多的資料和處理更大的負載。

分片的基本思想就是:
將集合切成小塊,這些塊分散到若干片裡,每個片只負責總資料的一部分。通過一個名為 mongos 的路由程式進行操作,mongos 知道資料和片的對應
關係(通過配置伺服器)。 大部分使用場景都是解決磁碟空間的問題,對於寫入有可能會變差(+++裡面的說明+++),查 詢則儘量避免跨分片查詢。使用分片的時機:

使用場景: 
1)機器的磁碟不夠用了。使用分片解決磁碟空間的問題。
2)單個mongod已經不能滿足寫資料的效能要求。通過分片讓寫壓力分散到各個分片上面,使用分片伺服器自身的資源。
3)想把大量資料放到記憶體裡提高效能。和上面一樣,通過分片使用分片伺服器自身的資源。

要構建一個MongoDB Sharding Cluster(分片叢集),需要三種角色:
1)分片伺服器(Shard Server)
   mongod 例項,用於儲存實際的資料塊,實際生產環境中一個 shard server 角色可由幾臺機器組個一個 relica set 承擔,防止主機單點故障
   這是一個獨立普通的mongod程式,儲存資料資訊。可以是一個副本集也可以是單獨的一臺伺服器。
2)配置伺服器(Config Server)
   mongod 例項,儲存了整個 Cluster Metadata,其中包括 chunk 資訊。
   這是一個獨立的mongod程式,儲存叢集和分片的後設資料,即各分片包含了哪些資料的資訊。最先開始建立,啟用日誌功能。像啟動普通的 mongod 一樣啟動
   配置伺服器,指定configsvr 選項。不需要太多的空間和資源,配置伺服器的 1KB 空間相當於真是資料的 200MB。儲存的只是資料的分佈表。
3)路由伺服器(Route Server)
   mongos例項,前端路由,客戶端由此接入,且讓整個叢集看上去像單一資料庫,前端應用
   起到一個路由的功能,供程式連線。本身不儲存資料,在啟動時從配置伺服器載入叢集資訊,開啟 mongos 程式需要知道配置伺服器的地址,指定configdb選項。

片鍵的意義
一個好的片鍵對分片至關重要。 片鍵必須是一個索引 ,通 過 sh.shardCollection 加會自動建立索引。一個自增的片鍵對寫入和資料均勻分佈就不是很好, 因為自增
的片鍵總會在一個分片上寫入,後續達到某個閥值可能會寫到別的分片。但是按照片鍵查詢會非常高效。隨機片鍵對資料的均勻分佈效果很好。注意儘量避免在多個分片上進行查詢。 
在所有分片上查詢,mongos 會對結果進行歸併排序

為何需要水平分片
1)減少單機請求數,將單機負載,提高總負載
2)減少單機的儲存空間,提高總存空間

mongodb sharding 伺服器架構

簡單註解:

分片叢集主要由三種元件組成:mongos,config server,shard
1) mongos  (路由程式, 應用程式接入 mongos 再查詢到具體分片)
資料庫叢集請求的入口,所有的請求都通過 mongos 進行協調,不需要在應用程式新增一個路由選擇器,mongos 自己就是一個請求分發中心,它負責把對應的資料請求
請求轉發到對應的 shard 伺服器上。在生產環境通常有多個 mongos 作為請求的入口,防止其中一個掛掉所有的 mongodb 請求都沒有辦法操作。

2) config server  (路由表服務。 每一臺都具有全部 chunk 的路由資訊)
顧名思義為配置伺服器,儲存所有資料庫元資訊(路由、分片)的配置。mongos 本身沒有物理儲存分片伺服器和資料路由資訊,只是快取在記憶體裡,配置伺服器則實際儲存
這些資料。mongos 第一次啟動或者關掉重啟就會從 config server 載入配置資訊,以後如果配置伺服器資訊變化會通知到所有的 mongos 更新自己的狀態,這樣 
mongos 就能繼續準確路由。在生產環境通常有多個 config server 配置伺服器,因為它儲存了分片路由的後設資料,這個可不能丟失!就算掛掉其中一臺,只要還有存貨, 
mongodb 叢集就不會掛掉。

3) shard  (為資料儲存分片。 每一片都可以是複製集(replica set))
這就是傳說中的分片了。如圖所示,一臺機器的一個資料表 Collection1 儲存了 1T 資料,壓力太大了!在分給 4 個機器後, 每個機器都是 256G,則分攤了集中在一臺
機器的壓力。事實上,上圖4個分片如果沒有副本集(replica set)是個不完整架構,假設其中的一個分片掛掉那四 分之一的資料就丟失了,所以在高可用性的分片架構還
需要對於每一個分片構建 replica set 副本集保 證分片的可靠性。生產環境通常是 2 個副本 + 1 個仲裁。

2)Sharding分割槽的原理

分片,是指將資料拆分,將其分散到不同的機器上。這樣的好處就是,不需要功能強大的大型計算機也可以儲存更多的資料,處理更大的負載。mongoDB 的分片,
是將collection 的資料進行分割,然後將不同的部分分別儲存到不同的機器上。當 collection 所佔空間過大時,我們需要增加一臺新的機器,分片會自動
將 collection 的資料分發到新的機器上。

mongoDB sharding分片的原理

人臉:    
代表客戶端,客戶端肯定說,你資料庫分片不分片跟我沒關係,我叫你幹啥就幹啥,沒什麼好商量的。

mongos: 
首先我們要了解”片鍵“的概念,也就是說拆分集合的依據是什麼?按照什麼鍵值進行拆分集合。mongos就是一個路由伺服器,它會根據管理員設定的"片鍵"
將資料分攤到自己管理的mongod叢集,資料和片的對應關係以及相應的配置資訊儲存在"config伺服器"上。
客戶端只需要對 mongos 進行操作就行了,至於如何進行分片,不需要 客戶端參與,由 mongos 和 config 來完成。

mongod:   
一個普通的資料庫例項或者副本集,如果不分片的話,我們會直接連上mongod。

分片是指將資料拆分,將其分散存在不同機器上的過程.有時也叫分割槽.將資料分散在不同的機器上MongoDB支援自動分片,可以擺脫手動分片的管理.叢集自動切分資料,做負載均衡

分片叢集的構造如下:

分片叢集由以下3個服務組成:
Shards Server: 每個shard由一個或多個mongod程式組成,用於儲存資料
Config Server: 用於儲存叢集的Metadata資訊,包括每個Shard的資訊和chunks資訊
Route Server: 用於提供路由服務,由Client連線,使整個Cluster看起來像單個DB伺服器

相關文章