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

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值是0100之間的數字,數字越大優先級越高,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 長度必須在 61024 個字符之間,
最好爲 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服務器

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