MongoDB副本集複製和分片

MongoDB是一個開源非關係文檔型數據庫。在MongoDB中的每一個記錄是一個文檔,文檔類似於JSON對象,它是一個由字段和值對組成的數據結構。

類似json的文檔存儲

類似json的文檔存儲

MongoDB出現解決了傳統關係型數據庫對海量數據的處理難題。以傳統的MySQL爲代表的關係型數據庫,事務保證操作和數據的可靠性,但同時也限制了數據的擴展性和數據庫海量數據的處理能力。MongoDB的數據庫不支持事務,這使它突破了關係型數據庫的侷限性,得到了良好的擴展性。在實現上MongoDB借鑑了MySQL,在操作方式上和工作模式與MySQL類似。

以下通過MongoDB副本集複製和分片,認識其對海量數據的處理和其原理:

MongoDB副本集複製

MongoDB在數據冗餘方面提供了兩種方案:

  • master/slave 主從複製
  • replica set 副本集複製

master/slave是和MySQL類似的一種複製方式,master端啓動一個I/O線程用於向slave端同步寫日誌文件,slave端啓動兩個線程,一個IO線程把日誌文件記錄在slave節點的中繼日誌中,SQL線程把中繼日誌進行回放完成備份,只是MySQL中的binlog在MongoDB叫做OpLog日誌文件。

這種複製方式最大的弊端在於:主節點成爲最大的單點所在,可能有人會說,給主節點做高可用,隨之而來是就是一堆問題:

  • 兩個主節點數據不應該也一樣嗎?兩個主節點又互爲單點所在
  • 當主節點A的宕機,A中的事務沒有執行完,B中的數據怎麼回滾?雙主節點事務難以同步

可以看出,事務在某些不必要的場景,反而帶來很多問題,於是MongoDB擺脫了事務的限制,提出了第二種方式replica set

副本集複製過程

enter description here

工作方式如下:
在MongoDB中一個副本集即爲服務於同一數據集的多個MongoDB實例,其中一個爲主節點Primary,其餘的都爲從節點Secondary(主節點上能夠完成讀寫操作,從節點僅能用於讀操作)。主節點記錄所有改變數據庫狀態的操作,將這些記錄保存在oplog中,oplog存儲在local數據庫,各個從節點通過此oplog來複制數據並應用於本地,保持本地的數據與主節點的一致。oplog具有冪等性(無論執行幾次其結果一致),比mysql的二進制日誌更高效可靠。
集羣中的各節點通過傳遞心跳信息(默認每2秒傳遞一次)來檢測各自的健康狀況。當主節點故障時多個從節點會觸發一次新的選舉操作,並選舉其中的一個成爲新的主節點(通常誰的優先級更高,誰就是新的主節點)。

各個從節點傳遞心跳

各個從節點傳遞心跳信息
沒有事務的限制,當主節點宕機時,每個從節點都可以作爲主節點。

實例

實驗環境

主機 IP地址
Primary 192.168.80.5
Secondary 192.168.80.8
Secondary 192.168.80.9

其配置較爲簡單,分爲以下幾個步驟:

  • 安裝配置MongoDB服務器端和客戶端

    yum install -y mongodb-server mongodb

    配置文件信息:

    [root@mongo1 ~]# vim /etc/mongod.conf 
    
    logpath=/var/log/mongodb/mongod.log 
    logappend=true 
    fork=true
    dbpath=/data/mongodb
    pidfilepath=/var/run/mongodb/mongod.pid
    bind_ip=0.0.0.0     # 服務監聽的地址
    httpinterface=true
    rest=true
    replSet=rs0         # 指定了副本集名稱,多個副本集用於區別
    replIndexPrefetch = _id_only    #指定副本集的索引預取,如果有預取功能可以讓複製過程更爲高效,
    
    # 有3個值none,_id_only,all。
    
    
    # none:不預取任何索引,
    
    
    # _id_only:預取ID索引,
    
    
    # all:預取所有索引。
    
  • 創建數據目錄啓動服務

    [root@mongo1 ~]# mkdir -pv /data/mongodb
    
    mkdir: created directory `/mongodb'
    mkdir: created directory `/data/mongodb'
    [root@mongo1 ~]# chown -R mongod.mongod /data/mongodb
    
    [root@mongo1 ~]# systemctl start mongod # 在每一個節點啓動服務
    
  • 配置添加集羣成員

    [root@mongo1 ~]# mongo --host 192.168.1.132
    MongoDB shell version: 2.6.5
    connecting to: 192.168.1.132:27017/test
    > rs.status()
    
    {
        "startupStatus" : 3,
        "info" : "run rs.initiate(...) if not yet done for the set",
        "ok" : 0,
        "errmsg" : "can't get local.system.replset config from self or any seed (EMPTYCONFIG)"
    }
    
    >rs.initiate()  #主節點初始化
    >rs.status()
    
    {
        "set" : "testSet",
        "date" : ISODate("2017-10-13T08:25:57Z"),
        "myState" : 1,
        "members" : [
            {
                "_id" : 0,
                "name" : "www.dearecho.me:27017",
                "health" : 1,
                "state" : 1,
                "stateStr" : "PRIMARY",
                "uptime" : 234,
                "optime" : Timestamp(1507883148, 1),
                "optimeDate" : ISODate("2017-10-13T08:25:48Z"),
                "electionTime" : Timestamp(1507883149, 1),
                "electionDate" : ISODate("2017-10-13T08:25:49Z"),
                "self" : true
            }
        ],
        "ok" : 1
    }
    
    rs0:PRIMARY> rs.add("192.168.80.8") # 發現前面的標識變爲了Primary,添加從節點
    { "ok" : 1 }
    rs0:PRIMARY> rs.add("192.168.80.9")
    { "ok" : 1 }
    
    在創建副本集時,有3種方式:
    db.runCommand( { replSetInitiate : <config_object> } )
    rs.initiate(<config_object>)
    rs.initiate()      #先在其中一個節點上初始化,再通過rs.add添加另外的節點
    這裏採用的是第一種方式。
  • 查看各節點信息

    > rs.status()
    {
        "set" : "rs0",
        "date" : ISODate("2015-09-04T23:02:13Z"),
        "myState" : 1,
        "members" : [                                                       #顯示副本集的所有成員信息
            {
                "_id" : 0,                                          #節點的標識符
                "name" : "192.168.80.5:27017",                      #節點名稱    
                "health" : 1,                                       #節點的健康狀態            
                "state" : 1,                                                                                
                "stateStr" : "PRIMARY",                             #該節點爲主節點                                
                "uptime" : 1750,                                    #運行時長                   
                "optime" : Timestamp(1507898695, 173),              #oplog最後一次操作的時間戳
                "optimeDate" : ISODate("2017-10-13T12:44:55Z"),
                "lastHeartbeat" : ISODate("2017-10-13T12:53:19Z"),
                "lastHeartbeatRecv" : ISODate("2017-10-13T12:53:19Z"),
                "pingMs" : 0,
                "electionTime" : Timestamp(1507899169, 1), #選舉日期                                  
                "self" : true                                       #表示是否爲當前節點
            },
            {
    
                "_id" : 1,                                          #節點的標識符
                "name" : "192.168.80.8:27017",                      #節點名稱    
                "health" : 1,                                       #節點的健康狀態            
                "state" : 1,                                                                                
                "stateStr" : "SECONDARY",                            #從節點                                
                "uptime" : 1750,                                    #運行時長                   
                "optime" : Timestamp(1507898695, 173),              #oplog最後一次操作的時間戳
                "optimeDate" : ISODate("2017-10-13T12:44:55Z"),
                "lastHeartbeat" : ISODate("2017-10-13T12:53:19Z"),
                "lastHeartbeatRecv" : ISODate("2017-10-13T12:53:19Z"),
                "pingMs" : 0,
                "syncingTo" : "192.168.80.5:27017"                  #指向的主節點
            },
            {
                "_id" : 2,                                         
                "name" : "192.168.80.9:27017",                         
                "health" : 1,                                                  
                "state" : 1,                                                                                
                "stateStr" : "SECONDARY",                            #從節點                                
                "uptime" : 1750,                                                       
                "optime" : Timestamp(1507898695, 173),              
                "optimeDate" : ISODate("2017-10-13T12:44:55Z"),
                "lastHeartbeat" : ISODate("2017-10-13T12:53:19Z"),
                "lastHeartbeatRecv" : ISODate("2017-10-13T12:53:19Z"),
                "pingMs" : 0,
                "syncingTo" : "192.168.80.5:27017"                  #指向的主節點
            }
        ],
        "ok" : 1
    }

測試

在主節點添加測試數據:

rs0:PRIMARY> use student_db 
switched to db student_db
rs0:PRIMARY> for (i=1;i<=100000;i++) db.students.insert({name:"student"+i,age:(i%120),address:"china_nb"});
WriteResult({ "nInserted" : 1 })

在從節點查看數據

rs0:SECONDARY> show collections  
2017-10-13T20:46:55.346+0800 error: { "$err" : "not master and slaveOk=false", "code" : 13435 } at src/mongo/shell/query.js:131

rs0:SECONDARY> rs.slaveOk()  # 需啓用從節點纔可查看
rs0:SECONDARY> db.students.findOne()
{
    "_id" : ObjectId("59e0b54660703b86d071762f"),
    "name" : "student1",
    "age" : 1,
    "address" : "china_nb"
}

主節點修改優先級

rs0:PRIMARY> cfg = rs.config()
{
    "_id" : "test",
    "version" : 3,
    "members" : [
        {
            "_id" : 0,
            "host" : "192.168.80.5:27017"
        },
        {
            "_id" : 1,
            "host" : "192.168.80.8:27017"
        },
        {
            "_id" : 2,
            "host" : "192.168.80.9:27017"
        }
    ]
}

rs0:PRIMARY> cfg.members[1].priority=2   # 設置第二個節點優先級爲2
2

rs0:PRIMARY> rs.reconfig(cfg)
2017-10-13T21:13:01.255+0800 DBClientCursor::init call() failed
2017-10-13T21:13:01.269+0800 trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed
2017-10-13T21:13:01.282+0800 reconnect 127.0.0.1:27017 (127.0.0.1) ok
reconnected to server after rs command (which is normal)

192.168.80.8查看狀態

rs0:PRIMARY> db.printReplicationInfo()
configured oplog size:   990MB
log length start to end: 1098secs (0.31hrs)
oplog first event time:  Fri Oct 13 2017 20:26:37 GMT+0800 (CST)
oplog last event time:   Fri Oct 13 2017 20:44:55 GMT+0800 (CST)
now:                     Fri Oct 13 2017 20:56:23 GMT+0800 (CST)

rs.status()
    {
        "_id" : 1,                  #節點2
        "name" : "192.168.80.8:27017",
        "health" : 1,
        "state" : 1,
        "stateStr" : "PRIMARY",     #已搶佔爲主節點
        "uptime" : 1602,
        "optime" : Timestamp(1507898695, 173),
        "optimeDate" : ISODate("2017-10-13T12:44:55Z"),
        "lastHeartbeat" : ISODate("2017-10-13T12:53:19Z"),
        "lastHeartbeatRecv" : ISODate("2017-10-13T12:53:19Z"),
        "pingMs" : 0,
        "electionTime" : Timestamp(1507899169, 1),
        "electionDate" : ISODate("2017-10-13T12:52:49Z")
    }

MongoDB數據分片

在Mongodb裏面存在另一種集羣,就是分片技術,可以滿足MongoDB數據量大量增長的需求。
當MongoDB存儲海量的數據時,一臺機器可能不足以存儲數據,也可能不足以提供可接受的讀寫吞吐量。這時,我們就可以通過在多臺機器上分割數據,使得數據庫系統能存儲和處理更多的數據。
MongoDB中使用分片集羣結構分佈:

enter description here
上圖中主要有如下所述三個主要組件:

  • Query Routers前端路由,客戶端由此接入,把客戶端的請求路由到合適的shared上。
  • Config Server實質爲mongod實例存儲了整個 ClusterMetadata,其中包括 chunk信息和索引信息。
  • Shard存儲實際的數據塊,實際生產環境中一個shard server角色可由幾臺機器組個一個replica set承擔,防止主機單點故障

分片過程:
把表上以某個字段爲例,字段創建爲索引,索引當做分片的元數據,而後把大數據切割成一個一個的chunk,把每個chunk分配到每個shared。在整個業務運行過程中,重新均衡,chunk在每個節點上挪來挪去。

實例

主機 IP
Query Routers 192.168.80.5
Config server 192.168.80.7
Shared 192.168.80.8, 192.168.80.9

實現步驟如下:

  • 配置各個節點
    配置config server

    yum install -y mongodb-server mongodb
    
    vim /etc/mongod.conf
    configsvr = true
    

    啓動其他節點:

    yum install -y mongodb-server mongodb
    
    mongos --configdb=192.168.80.7 --fork --logpath=/var/log/mongodb/mongo.log   # 啓動Query Routers 
    
    2017-10-14T10:32:40.251+0800 warning: running with 1 config server should be done only for testing purposes and is not recommended for production
    about to fork child process, waiting until server is ready for connections.
    forked process: 16035
    child process started successfully, parent exiting
    
    [root@node ~]# ss -tnl
    LISTEN      0      128     *:27017               *:*    # monogs監聽在27017 
    
    
    
    systemctl start mongod  # 啓動shared
  • Query Routers配置

    節點加入`Shard`:
    mongos> sh.addShard("192.168.80.8")
    { "shardAdded" : "shard0000", "ok" : 1 }
    mongos> sh.addShard("192.168.80.9")
    { "shardAdded" : "shard0001", "ok" : 1 }
    
    mongos> use student_db  # 創建數據庫
    switched to db student_db
    
    mongos>  sh.enableSharding("student_db") # 數據庫啓用分片
    { "ok" : 1 }
    
    
    mongos> sh.shardCollection("student_db.students",{"age":1}) # 創建collections,並指明索引
    { "collectionsharded" : "student_db.students", "ok" : 1 }
    
       mongos> sh.status()
    --- Sharding Status --- 
      sharding version: {
        "_id" : 1,
        "version" : 4,
        "minCompatibleVersion" : 4,
        "currentVersion" : 5,
        "clusterId" : ObjectId("59e17748c68473e873f81bc7")
    }
      shards:
        {  "_id" : "shard0000",  "host" : "192.168.80.8:27017" }
        {  "_id" : "shard0001",  "host" : "192.168.80.9:27017" }
      databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
        {  "_id" : "test",  "partitioned" : true,  "primary" : "shard0001" }
        {  "_id" : "student",  "partitioned" : false,  "primary" : "shard0000" }
        {  "_id" : "student_db",  "partitioned" : true,  "primary" : "shard0000" } #數據庫分片已啓用

測試

#插入數據
mongos> for (i=1;i<=100000;i++) db.students.insert({name:"student"+i,age:(i%120),address:"china_nb"});

mongos>sh.status()  # 查看數據庫分片狀態
        student_db.students
        shard key: { "age" : 1 }
        chunks:     
            shard0000   2  # 數據被分佈在每一個shared上
            shard0001   1
        { "age" : { "$minKey" : 1 } } -->> { "age" : 1 } on : shard0001 Timestamp(2, 0) 
        { "age" : 1 } -->> { "age" : 119 } on : shard0000 Timestamp(2, 2) 
        { "age" : 119 } -->> { "age" : { "$maxKey" : 1 } } on : shard0000 Timestamp3, 3) 

總結

Mongodb在丟棄了關係型數據庫一些侷限性:事務 關係。同時採用文檔形式對數據的存儲,加大數據存儲的同時加快數據的查詢效率。此外Mongodb還提供完備的HA解決方案和分片的分佈式策略。在不依賴於事務的大數據場景中,讓其作爲大數據處理有效的解決方案之一。

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