MongoDB主從複製,副本集, Sharding

朝聞道-夕死可矣發表於2017-08-12

一,主從配置

主從模式可以用於備份,故障恢復,讀擴充套件等,可以配置一個主節點一個從節點或者多個從節點;

主節點只有一個

我們在一臺伺服器上實現主從模式

master.conf

dbpath=I:\mongodb\masterdata
port=8888 #埠號 預設為27017
bind_ip=127.0.0.1
master=true
slave.conf 配置

dbpath=I:\mongodb\slavedata
port=9999 #埠號 預設為27017
bind_ip=127.0.0.1
source=127.0.0.1:8888
slave=true
啟動:主節點-〉從節點

mongod --config I:\mongodb\master.conf

mongod --config I:\mongodb\slave.conf

最後啟動兩個終端連線

master操作:

> use db1;
switched to db db1
> function add(){
... var i=0;
... for (;i<50;i++){
...   db.c1.insert({"name":"doc"+i})
... }
... }
> add()
> db.c1.findOne()
{ "_id" : ObjectId("5986d80d25fe8e60ed262340"), "name" : "doc0" }
> db.createUser({user:'admin1',pwd:'admin1',roles:[{role:"root",db:"admin"}]})
Successfully added user: {
        "user" : "admin1",
        "roles" : [
                {
                        "role" : "root",
                        "db" : "admin"
                }
        ]
}
> db.auth('admin1','admin1')
1
slave操作

> show dbs;
2017-08-06T16:59:32.179+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
> db.createUser({user:'admin1',pwd:'admin1',roles:[{role:"root",db:"admin"}]})
2017-08-06T16:59:38.354+0800 E QUERY    [thread1] Error: couldn't add user: not
master :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype.createUser@src/mongo/shell/db.js:1290:15
@(shell):1:1
> rs.slaveOk()
> show dbs;
admin  0.000GB
db1    0.000GB
local  0.000GB
> use db1;
switched to db db1
> db.c1.findOne()
{ "_id" : ObjectId("5986d80d25fe8e60ed262340"), "name" : "doc0" }
其中下邊的命令如果不使用,預設slave是禁止讀寫的
rs.slaveOk()

複製原理

在主從結構中,主節點的操作記錄成為oplog。oplog儲存在一個系統資料庫local的集合oplog.$main中,這個集合的每個文件都代表主節點上執行的一個操作。
從伺服器會定期從主伺服器中獲取oplog記錄,然後在本機上執行!對於儲存oplog的集合,MongoDB採用的是固定集合,也就是說隨著操作過多,新的操作會覆蓋舊的操作!
主從複製的其他設定項
–only 從節點指定複製某個資料庫,預設是複製全部資料庫
–slavedelay 從節點設定主資料庫同步資料的延遲(單位是秒)
–fastsync 從節點以主資料庫的節點快照為節點啟動從資料庫
–autoresync 從節點如果不同步則從新同步資料庫(即選擇當通過熱新增了一臺從伺服器之後,從伺服器選擇是否更新主伺服器之間的資料)
–oplogSize 主節點設定oplog的大小(主節點操作記錄儲存到local的oplog中)

> use local
switched to db local
> db.sources.find()
> show tables;
me
oplog.$main
startup_log
> show collections;
me
oplog.$main
startup_log
從節點,存在一個集合sources。這個集合就儲存了我這個伺服器的主伺服器是誰
> use local
switched to db local
> show tables;
me
sources
startup_log
> db.sources.find()
{ "_id" : ObjectId("5986d64779fd5d5f97e126c0"), "host" : "127.0.0.1:8888", "sour
ce" : "main", "syncedTo" : Timestamp(1502010374, 1) }
可以在從節點上動態操作主從關係,

掛載到某主節點下
db.sources.insert({“host”:”127.0.0.1:8888”})

刪除掛在的主節點

db.sources.remove({“host”:”127.0.0.1:8888”})

二、副本集

副本集類似主從複製,但是有些區別,主要區別是:主從複製在主機當機後所有服務將停止,而副本集在主機當機後,副本會接管主節點成為主節點,不會出現當機的情況。

副本集叢集中沒有特定的主資料庫;如果哪個主資料庫,叢集中就會推選出一個從屬資料庫作為主資料庫頂上,這就具備了自動故障恢復功能

副本及特徵:

    N 個節點的叢集
    任何節點可作為主節點
    所有寫入操作都在主節點上
    自動故障轉移
    自動恢復

三個配置檔案

C1.conf

dbpath=I:\mongodb\C1
port=1001 #埠號
bind_ip=127.0.0.1 #服務地址
replSet=rs1/127.0.0.1:1002 #設定同伴,rs1位叢集名稱
C2.conf

dbpath=I:\mongodb\C2
port=1002 #埠號
bind_ip=127.0.0.1
replSet=rs1/127.0.0.1:1003
C3.conf

dbpath=I:\mongodb\C3
port=1003 #埠號 
bind_ip=127.0.0.1
replSet=rs1/127.0.0.1:1001
這個配置使得三臺伺服器組成一個閉環,依次啟動三個伺服器

初始化副本集

> config = {_id: 'rs1', members: [{
... "_id":1,
... "host":"127.0.0.1:1001"
... },{
... "_id":2,
... "host":"127.0.0.1:1002"
... },{
... "_id":3,
... "host":"127.0.0.1:1003"
... }]
... }
{
        "_id" : "rs1",
        "members" : [
                {
                        "_id" : 1,
                        "host" : "127.0.0.1:1001"
                },
                {
                        "_id" : 2,
                        "host" : "127.0.0.1:1002"
                },
                {
                        "_id" : 3,
                        "host" : "127.0.0.1:1003"
                }
        ]
}
> rs.initiate(config)
{ "ok" : 1 }
可以使用rs.status(),來檢視副本集狀態

後邊看到初始化之後,命令列變為:rs1:PRIMARY>,如果重新啟動連線第一個會變為:rs1:PRIMARY>,表示活動的節點,其他未備份節點;只有活躍節點才能進行查詢資料庫的資訊操作,備份節點不能

三、分片

在Mongodb裡面存在另一種叢集,類似於資料庫表中的分割槽概念,就是分片技術,可以滿足MongoDB資料量大量增長的需求;

當MongoDB儲存海量的資料時,一臺機器可能不足以儲存資料,也可能不足以提供可接受的讀寫吞吐量。我們可以通過在多臺機器上分割資料,使得資料庫系統能儲存和處理更多的資料,這就是資料的水平擴充套件

使用分片的理由:

 複製時所有的寫入都是在主節點

 副本集限制在12節點

 請求量大時會出現記憶體不足

 本地磁碟不足

 垂直擴充套件成本高

官網說明:

  • shard: Each shard contains asubset of the sharded data. Each shard can be deployed as a replicaset.
  • mongos: The mongos acts as aquery router, providing an interface between client applications and thesharded cluster.
  • config servers: Configservers store metadata and configuration settings for the cluster. Asof MongoDB 3.4, config servers must be deployed as a replica set (CSRS).

解釋:

四個元件:mongos、config server、shard、replica set。

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

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

shard:分片,臺機器的一個資料表 Collection1 儲存了 1T 資料,壓力太大了!在分給4個機器後,每個機器都是256G,則分攤了集中在一臺機器的壓力。也許有人問一臺機器硬碟加大一點不就可以了,為什麼要分給四臺機器呢?不要光想到儲存空間,實際執行的資料庫還有硬碟的讀寫、網路的IO、CPU和記憶體的瓶頸。在mongodb叢集只要設定好了分片規則,通過mongos運算元據庫就能自動把對應的資料操作請求轉發到對應的分片機器上。在生產環境中分片的片鍵可要好好設定,這個影響到了怎麼把資料均勻分到多個分片機器上,不要出現其中一臺機器分了1T,其他機器沒有分到的情況,這樣還不如不分片!

replica set:前面已經詳細講過了這個東東,怎麼這裡又來湊熱鬧!其實上圖4個分片如果沒有 replica set 是個不完整架構,假設其中的一個分片掛掉那四分之一的資料就丟失了,所以在高可用性的分片架構還需要對於每一個分片構建 replica set 副本集保證分片的可靠性。生產環境通常是 2個副本 + 1個仲裁。

下面演示下如何搭建高可用的mongodb叢集:

首先確定各個元件的數量,mongos 3個, config server 3個,資料分3片 shard server 3個,每個shard 有一個副本一個仲裁也就是 3 * 2 = 6 個,總共需要部署15個例項。這些例項可以部署在獨立機器也可以部署在一臺機器
本例項搭建在一個伺服器上

搭建例項

下邊的配置檔案路徑不再說明


1,規劃5個元件對應的埠號,由於一個機器需要同時部署 mongos、config server 、shard1、shard2、shard3,所以需要用埠進行區分。
這個埠可以自由定義,在本文 mongos為 20000, config server 為 21000 , shard1為 22001 , shard2為22002, shard3為22003

2,啟動三個配置伺服器

C:\Users\Administrator>mongod --configsvr --replSet cfgReplSet --dbpath I:\mongodb\shardtest\config\data1  --port 21000 
C:\Users\Administrator>mongod --configsvr --replSet cfgReplSet --dbpath I:\mongodb\shardtest\config\data2  --port 21001 
C:\Users\Administrator>mongod --configsvr --replSet cfgReplSet --dbpath I:\mongodb\shardtest\config\data3  --port 21002
連線任意一臺配置伺服器,建立配置伺服器副本集
mongo --host 127.0.0.1 --port 21000
rs.initiate({_id:"cfgReplSet",configsvr:true,members:[{_id:0,host:"127.0.0.1:21000"},{_id:1,host:"127.0.0.1:21001"},{_id:2,host:"127.0.0.1:21002"}]})
C:\Users\Administrator>mongo --host 127.0.0.1 --port 21000
MongoDB shell version v3.4.5
connecting to: mongodb://127.0.0.1:21000/
MongoDB server version: 3.4.5
Server has startup warnings:
2017-08-12T17:14:32.377+0800 I CONTROL  [initandlisten]
2017-08-12T17:14:32.379+0800 I CONTROL  [initandlisten] ** WARNING: Access contr
ol is not enabled for the database.
2017-08-12T17:14:32.381+0800 I CONTROL  [initandlisten] **          Read and wri
te access to data and configuration is unrestricted.
2017-08-12T17:14:32.383+0800 I CONTROL  [initandlisten]
> rs.initiate({_id:"cfgReplSet",configsvr:true,members:[{_id:0,host:"127.0.0.1:2
1000"},{_id:1,host:"127.0.0.1:21001"},{_id:2,host:"127.0.0.1:21002"}]}))
{ "ok" : 1 }
cfgReplSet:OTHER>
同樣操作一次shard2,shard3

3,每一臺伺服器分別啟動分片及副本集

mongod --shardsvr --replSet shard1ReplSet --port 22001 --dbpath I:\mongodb\shardtest\shard1data\d1   --nojournal
mongod --shardsvr --replSet shard1ReplSet --port 22011 --dbpath I:\mongodb\shardtest\shard1data\d2   --nojournal
mongod --shardsvr --replSet shard1ReplSet --port 22021 --dbpath I:\mongodb\shardtest\shard1data\d3   --nojournal
連線任意一臺分片伺服器,建立副本集並初始化
mongo --host 127.0.0.1 --port 22001
rs.initiate({_id:"shard1ReplSet",members:[{_id:0,host:"127.0.0.1:22001"},{_id:1,host:"127.0.0.1:22011"},{_id:2,host:"127.0.0.1:22021"}]})
比如:

C:\Users\Administrator>mongo --host 127.0.0.1 --port 22001
MongoDB shell version v3.4.5
connecting to: mongodb://127.0.0.1:22001/
MongoDB server version: 3.4.5
Server has startup warnings:
2017-08-12T17:44:50.789+0800 I CONTROL  [initandlisten]
2017-08-12T17:44:50.790+0800 I CONTROL  [initandlisten] ** WARNING: Access contr
ol is not enabled for the database.
2017-08-12T17:44:50.792+0800 I CONTROL  [initandlisten] **          Read and wri
te access to data and configuration is unrestricted.
2017-08-12T17:44:50.793+0800 I CONTROL  [initandlisten]
> rs.initiate({_id:"shard1ReplSet",members:[{_id:0,host:"127.0.0.1:22001"},{_id:
1,host:"127.0.0.1:22011"},{_id:2,host:"127.0.0.1:22021"}]})
{ "ok" : 1 }
shard1ReplSet:OTHER>
另外兩臺:

mongod --shardsvr --replSet shard2ReplSet --port 22002 --dbpath I:\mongodb\shardtest\shard2data\d1   --nojournal
mongod --shardsvr --replSet shard2ReplSet --port 22012 --dbpath I:\mongodb\shardtest\shard2data\d2   --nojournal
mongod --shardsvr --replSet shard2ReplSet --port 22022 --dbpath I:\mongodb\shardtest\shard2data\d3   --nojournal
mongo --host 127.0.0.1 --port 22002
rs.initiate({_id:"shard2ReplSet",members:[{_id:0,host:"127.0.0.1:22002"},{_id:1,host:"127.0.0.1:22012"},{_id:2,host:"127.0.0.1:22022"}]})


mongod --shardsvr --replSet shard3ReplSet --port 22003 --dbpath I:\mongodb\shardtest\shard3data\d1   --nojournal
mongod --shardsvr --replSet shard3ReplSet --port 22013 --dbpath I:\mongodb\shardtest\shard3data\d2   --nojournal
mongod --shardsvr --replSet shard3ReplSet --port 22023 --dbpath I:\mongodb\shardtest\shard3data\d3   --nojournal
mongo --host 127.0.0.1 --port 22003
rs.initiate({_id:"shard3ReplSet",members:[{_id:0,host:"127.0.0.1:22003"},{_id:1,host:"127.0.0.1:22013"},{_id:2,host:"127.0.0.1:22023"}]})
選擇一臺伺服器啟動mongos路由服務

mongos  --configdb cfgReplSet/127.0.0.1:21000,127.0.0.1:21001,127.0.0.1:21002 --port  20000

登陸路由服務客戶端
mongo --host 127.0.0.1 --port 20000
新增分片到叢集
sh.addShard("shard1ReplSet/127.0.0.1:22001,127.0.0.1:22011,127.0.0.1:22021")
一次新增shard2 shard3
sh.addShard("shard2ReplSet/127.0.0.1:22002,127.0.0.1:22012,127.0.0.1:22022")
sh.addShard("shard3ReplSet/127.0.0.1:22003,127.0.0.1:22013,127.0.0.1:22023")

enable sharding for a database:

mongos> sh.enableSharding("test")
{ "ok" : 1 }
shard a collection:

mongos> sh.shardCollection("test.Log",{id:1})
{ "collectionsharded" : "test.Log", "ok" : 1 }

測試

mongos> use test
switched to db test
mongos> for (var i=1; i<=100000; i++){
...     db.Log.save({id:i,"msg":"msg"+i});
... }
WriteResult({ "nInserted" : 1 })

檢視狀態

mongos> db.Log.stats()
{
        "sharded" : true,
        "capped" : false,
        "ns" : "test.Log",
        "count" : 100000,
        "size" : 5188895,
        "storageSize" : 1679360,
        "totalIndexSize" : 2183168,
        "indexSizes" : {
                "_id_" : 942080,
                "id_1" : 1241088
        },
        "avgObjSize" : 51,
        "nindexes" : 2,
        "nchunks" : 3,
        "shards" : {
                "shard1ReplSet" : {
                        "ns" : "test.Log",
                        "size" : 5188895,
                        "count" : 100000,
                        "avgObjSize" : 51,
                        。。。
如果想均勻分佈在不同的分片上,可以使用sharding key的策略為hashed,重新插入資料
sh.shardCollection("test.Log",{id:"hashed"}),然後插入資料即可,就可以保持資料的均衡




相關文章