程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Mongodb源碼分析--Replication之主從模式--Master

Mongodb源碼分析--Replication之主從模式--Master

編輯:關於C語言

mongodb中提供了復制(Replication)機制,通過該機制可以幫助我們很容易實現讀寫分離方案,並支持災難恢復(服務器斷電)等意外情況下的數據安全。

      在老版本(1.6)中,Mongo提供了兩種方式的復制:master-slave及replica pair模式(注:mongodb最新支持的replset復制集方式可看成是pair的升級版,它解決pair只能在兩個結點間同步的限制,支持多個結點同步且支持主從宕機時的自動切換, 在1.6版以後提供)。
\

      利用前者,我們可以實現讀寫分離(主從復制模式),後者則支持當主服務器斷電情況下的集群中其它slave自動接管,並升級為主服務器。 並且如果後來的也出錯了,那麼master狀態將會轉回給第一個服務器(之前宕機但後來又恢復運行的服務器)。

      同時mongodb支持使用安全認證(enable)。不管哪種replicate方式,只要在master/slave中創建一個能為各個database認識的用戶名/密碼即可。其認證過程如下:

    slave先在local.system.users裡查找一個名為"repl"的用戶,找到後用它去認證master。如果"repl"用戶沒有找到,則使用local.system.users中的第一個用戶去認證。local數據庫和admin數據庫一樣,local中的用戶可以訪問整個db server。


    下面介紹分別介紹一下這兩種復制的配置方式:
   
    Master-Slave(主從)模式:
     一個server可以同時為master和slave。一個slave可以有多個master(不推薦,可能會產生不可預期的結果)。

     配置選項:
     --master  以主服務器方式啟動
     --slave   以從服務器方式啟動
     --autoresync:自動重新sync,因為該操作會copy 主服務器上的所有document,比較耗時,在10分鐘內最多只會進行一次。
     --oplogSize:指定master上用於存放更改的數據量,如果不指定,在32位機上最少為50M,在64位機上最少為 1G,最大為磁盤空間的5%。
     --source  主服務器地址(與--slave組合使用)
     --only    僅限於同步指定數據庫(下面示例為test庫)
     --slavedelay  同步延時

     下面是本人在本地為了測試方便所使用的配置參數
        Master:  IP->10.0.1.103       

mongod --dbpath=d:mongodbdb --master --oplogSize 64
        Slave:   IP->10.0.4.210
       

mongod --dbpath=d:mongodbdb --slave --source 10.0.1.103:27017 --only test --slavedelay 100

    

    補充:受限的master-master復制,這種模式對插入、查詢及根據_id進行的刪除操作都是安全的。但對同一對象的並發更新無法進行。Mongo 不支持完全的master-master復制,通常情況下不推薦使用master-master模式,但在一些特定的情況下master-master也可用。master-master也只支持最終一致性。配置master-master只需運行mongod時同時加上--master選項和 --slave選項。如下:
     mongod --dbpath=d:mongodbdb --port 27017 --master --slave --source localhost:27018
     mongod --dbpath=d:mongodbdb --port 27018 --master --slave --source localhost:27017

     
    Replica pairs模式
     以這種方式啟動後,數據庫會自動協商誰是master誰是slave。一旦一個數據庫服務器斷電,另一個會自動接管,並從那一刻起起為master。萬一另一個將來也出錯了,那麼master狀態將會轉回給第一個服務器。以這種復制方式啟動mongod的命令如下:
     配置選項:
      mongod --pairwith <remoteserver> --arbiter <arbiterserver>
      --pairwith: remoteserver是pair裡的另一個server
      --arbiter:  arbiterserver是一個起仲裁作用的Mongo數據庫,用來協商pair中哪一個是master。arbiter運行在第三個機器上,利用“平分決勝制”決定在pair中的兩台機器不能聯系上對方時讓哪一個做master,一般是能同arbiter通話的那台機器做master。如果不加--arbiter選項,出現網絡問題時兩台機器都作為master。
      注:可使用db.$cmd.findOne({ismaster:1})可以檢查當前哪一個database是master。

     另外這種模式下的兩台機器只能滿足最終一致性。當replica pair中的一台機器完全掛掉時,需要用一台新的來代替。如(n1, n2)中的n2掛掉,這時用n3來代替n2。步驟如下:
      1. 告訴n1用n3來代替n2:db.$cmd.findOne({replacepeer:1});
      2. 重啟n1讓它同n3對話:mongod --pairwith n3 --arbiter <arbiterserver>
      3. 啟動n3:mongod --pairwith n1 --arbiter <arbiterserver>。
     在n3的數據沒有同步到n1前n3還不能做master,這個過程長短由數據量的多少決定。
 
   
     了解了復制模式之後,還有一個問題需要介紹一下,不是就是本文中mongodb使用cap collection來存儲操作日志,並進而使用日志來復制(同步)結點間的數據,其中由主結點保存的操作的記錄叫做oplog(operation log的簡稱)。
 
   Oplog存在一個叫local的特殊數據庫中,在oplog.$main集合。Oplog中的每一個文檔表示一個在主結點上執行的操作。文檔主要包括4塊內容,如下:
  

 Ts:操作的時間戳。時間戳類型是一個用來跟蹤操作是何時執行的一種內部類型。它由4字節的時間戳和四字節的增量計數器組成。
 Op:執行的操作的類型,大小為1字節。(例如,“i”代表insert,"u":update, "d":delete, "n":none無操作等)
 Ns:執行操作的命名空間(集合名)
 O:執行操作的文檔。對於插入,這是將要插入的文檔。

     另外這種日志只保存會“改變數據庫狀態”的操作。查詢操作不會記錄在oplog中。


     好了,了解這些知識之後,我們就來開始看一下如何調試master-slave模式的源碼,首先要在vs2010中打開mongod項目,並將啟動參數中設置如下:
      --master --oplogSize 64   (master IP為10.0.1.103)

     如下圖:
  
 \

     之後編譯該項目,啟動該主服務結點,如下:

  \  
     
     接著我們可以在本地或另外一台機器上啟動一個slave結點:

  mongod --dbpath=d:mongodbdb --slave --source 10.0.1.103:27017 --only test --slavedelay 100


   下面介紹一下master(主服務端)的代碼執行流程。首先我們打開instance.cpp文件,找到下面方法:
  

    //instance.cpp
    // Returns false when request includes end
    void assembleResponse( Message &m, DbResponse &dbresponse, const SockAddr &client ) {
    ......
       if ( op == dbQuery ) {
            if ( handlePossibleShardedMessage( m , &dbresponse ) )
                return;
            receivedQuery(c , dbresponse, m );
        }
        //服務端(master) 收到message執行相關查詢操作
        else if ( op == dbGetMore ) {
            if ( ! receivedGetMore(dbresponse, m, currentOp) )
                log = true;
        }
    .....
    }

&

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved