Mongodb主從復制/ 副本集/分片集群介紹


 

前面的文章介紹了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服務器


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM