MongoDBマスターコピーとコピーセット


MongoDBレプリケーション


コンセプト


レプリケーションは、複数のサーバ間でデータを同期するプロセスです.

レプリケーションの特徴

  • データの安全性を保障する
  • データ高可用性
  • DR
  • バックアップ、インデックスの再構築、圧縮などのダウンタイム・メンテナンスを必要としない
  • 分散型読み取りデータ(読み取り能力の向上)mongodbは、レプリカセットとプライマリ・スレーブ・レプリケーションをサポートし、プライマリ・スレーブ・レプリケーションは公式に推奨されていません(自動フェイルオーバーはサポートされていません).

  • マスターコピー


    プライマリ・スレーブ・レプリケーションは、バックアップ、障害復旧、読み取り拡張などに使用できます.基本的には、プライマリ・ノードと1つ以上のセカンダリ・ノードを構築し、データベース・クラスタでプライマリ・サーバが誰であるかを明確に知る必要があります.プライマリ・サーバは1台しかありません.サーバからは、自分のデータ・ソース、すなわち対応するプライマリ・サービスが誰であるかを知る必要があります.mongod --masterを実行してプライマリ・サーバを起動します.mongod --slave --source master_addressを実行してサーバから起動します.

    構成の調整


    主にdbPathとlog pathを指定し、masterは以下のように構成されています.
    $ cat master.conf
    # mongod.conf
    
    # for documentation of all options, see:
    #   http://docs.mongodb.org/manual/reference/configuration-options/
    
    # Where and how to store data.
    storage:
      dbPath: /home/work/mongo/master
      journal:
        enabled: true
    #  engine:
    #  mmapv1:
    #  wiredTiger:
    
    # where to write logging data.
    systemLog:
      destination: file
      logAppend: true
      path: /home/work/mongo/master/mongod.log
    
    # network interfaces
    net:
      port: 27017
      bindIp: 127.0.0.1

    mongoインスタンスの起動

    mongod -f master.conf --logappend --master --fork
    mongod -f slave-1.conf --logappend --slave --fork --source localhost:27017
    mongod -f slave-2.conf --logappend --slave --fork --source localhost:27017

    すべてのスレーブノードはプライマリノードから情報をコピーし、ノードからスレーブノードへのメカニズムをコピーすることはできません.これは、ノードに独自のoplogがないためです.1つのクラスタではノードから明確な制限はないが,複数のノードが単一のホストに対して開始したクエリも耐えられず,12ノードを超えないクラスタは良好に動作する.

    機能テスト


    プライマリ・ライブラリにuserセットにデータを挿入します.
    > use test
    switched to db test
    > db.user.insert({'u':'test', 'p':'pwd'})
    WriteResult({ "nInserted" : 1 })
    > db.user.find()
    { "_id" : ObjectId("59c8d10c836ac6ec4e425fc5"), "u" : "test", "p" : "pwd" }

    ライブラリからログインすると、userセットが挿入したレコードが同期していることがわかります.
    > rs.slaveOk()
    > show dbs
    admin  0.000GB
    local  0.000GB
    test   0.000GB
    > use test
    switched to db test
    > show collections;
    user
    > db.user.find()
    { "_id" : ObjectId("59c8d10c836ac6ec4e425fc5"), "u" : "test", "p" : "pwd" }

    その他の構成アイテム

  • –onlyノードからデータベースのコピーを指定します.デフォルトでは、すべてのデータベースをコピーします.
  • –slavedelayノードからプライマリ・データベースの同期データを設定する遅延(秒単位)
  • –fastsyncスレーブノードマスターデータベースのノードスナップショットをノードとして起動スレーブデータベース
  • –autoresyncスレーブノードが同期していない場合は、新しい同期データベースから(すなわち、スレーブサーバをホットで追加した後、サーバからプライマリサーバ間のデータを更新するかどうかを選択する)
  • .
  • –oplogSizeプライマリノードはoplogのサイズを設定(プライマリノード操作記録はlocalのoplogに格納)、単位MB
  • レプリカセット(Replica Set)


    レプリカセットは、1つのprimaryノードと1つ以上のsecondaryノードからなる自動フェイルオーバ機能を持つプライマリ・スレーブ・クラスタです.プライマリ・スレーブ・クラスタとレプリカ・セットの最も明らかな違いは、レプリカ・セットに固定されたプライマリ・ノードがないことです.クラスタ全体がプライマリ・ノードを選択し、動作しない場合は他のノードに変更します.レプリカセットには、アクティブなノードと1つ以上のバックアップノードがあります.

    さぎょうげんり

  • mongodbのレプリケーションには、少なくとも2つのインスタンスが必要です.1つはプライマリノードであり、クライアント要求の処理を担当し、残りはセカンダリノードであり、プライマリノード上のデータのコピーを担当します.プライマリノードは、その上に記録されたすべての操作oplogを記録し、ノードから定期的にプライマリノードをポーリングしてこれらの操作を取得し、自分のデータのコピーに対してこれらの操作を実行し、ノードからのデータがプライマリノードと一致することを保証します.
  • プライマリノードの操作レコードはoplog(operation log)と呼ばれ、localデータベースに格納される(localデータベースはコピーされず、コピー状態情報を格納するために使用される).oplogの各ドキュメントは、プライマリノードで実行される操作を表します.oplogは,ノードからプライマリノードとデータ同期を維持するメカニズムとしてのみ用いられる.
  • oplog.rsは固定長のcapped collectionである.デフォルトでは、64ビットのインスタンスは、localデータベースに割り当てられ、サーバの起動時に予め割り当てられたoplog 5%の空き領域を使用します.
  • ノードからプライマリノードに遅れている場合、oplogログがノードから実行されていないため、oplogがロールアップされている可能性があります.ノードからプライマリノードに追いつかず、レプリケーションが停止します.ノードから完全な同期を再実行する必要がある場合は、{resync:1}コマンドを使用して再同期を手動で実行するか、スレーブノードの起動時に–autoresyncオプションを指定して自動的に再同期させることができます.再同期のコストは高価で、できるだけ避けるべきで、避ける方法は十分なoplogを構成することです.

  • インスタンスの起動


    rep 1,2,3はそれぞれ270127070701827019ポートで起動した.
    work@jdu4e00u53f7:~/mongo$ mongod -f rep1.conf --replSet rs0 --fork
    work@jdu4e00u53f7:~/mongo$ mongod -f rep2.conf --replSet rs0 --fork
    work@jdu4e00u53f7:~/mongo$ mongod -f rep3.conf --replSet rs0 --fork
    work@jdu4e00u53f7:~/mongo$ mongo --port 27017

    レプリカセットの初期化

    > rsconf = {'_id':'rs0', 'members':[{'_id': 0, 'host':'127.0.0.1:27017'}, {'_id': 1, 'host': '127.0.0.1:27018'}, {'_id':2, 'host': '127.0.0.1:27019'}]}
    {
        "_id" : "rs0",
        "members" : [
            {
                "_id" : 0,
                "host" : "127.0.0.1:27017"
            },
            {
                "_id" : 1,
                "host" : "127.0.0.1:27018"
            },
            {
                "_id" : 2,
                "host" : "127.0.0.1:27019"
            }
        ]
    }
    > rs.initiate(rsconf)
    { "ok" : 1 }

    現在の構成の表示

    rs0:OTHER> rs.conf()
    {
        "_id" : "rs0",
        "version" : 1,
        "protocolVersion" : NumberLong(1),
        "members" : [
            {
                "_id" : 0,
                "host" : "127.0.0.1:27017",
                "arbiterOnly" : false,
                "buildIndexes" : true,
                "hidden" : false,
                "priority" : 1,
                "tags" : {
    
                },
                "slaveDelay" : NumberLong(0),
                "votes" : 1
            },
            {
                "_id" : 1,
                "host" : "127.0.0.1:27018",
                "arbiterOnly" : false,
                "buildIndexes" : true,
                "hidden" : false,
                "priority" : 1,
                "tags" : {
    
                },
                "slaveDelay" : NumberLong(0),
                "votes" : 1
            },
            {
                "_id" : 2,
                "host" : "127.0.0.1:27019",
                "arbiterOnly" : false,
                "buildIndexes" : true,
                "hidden" : false,
                "priority" : 1,
                "tags" : {
    
                },
                "slaveDelay" : NumberLong(0),
                "votes" : 1
            }
        ],
        "settings" : {
            "chainingAllowed" : true,
            "heartbeatIntervalMillis" : 2000,
            "heartbeatTimeoutSecs" : 10,
            "electionTimeoutMillis" : 10000,
            "catchUpTimeoutMillis" : 60000,
            "getLastErrorModes" : {
    
            },
            "getLastErrorDefaults" : {
                "w" : 1,
                "wtimeout" : 0
            },
            "replicaSetId" : ObjectId("59c8e527002aa17b30c3408c")
        }
    }

    mongodbでは、構成内のノードがレプリカセットrs 0に自動的に追加されます.

    プライマリノードの表示

    rs0:SECONDARY> rs.isMaster()
    {
        "hosts" : [
            "127.0.0.1:27017",
            "127.0.0.1:27018",
            "127.0.0.1:27019"
        ],
        "setName" : "rs0",
        "setVersion" : 1,
        "ismaster" : false,
        "secondary" : true,
        "primary" : "127.0.0.1:27017",
        "me" : "127.0.0.1:27018",
        "lastWrite" : {
            "opTime" : {
                "ts" : Timestamp(1506338789, 1),
                "t" : NumberLong(1)
            },
            "lastWriteDate" : ISODate("2017-09-25T11:26:29Z")
        },
        "maxBsonObjectSize" : 16777216,
        "maxMessageSizeBytes" : 48000000,
        "maxWriteBatchSize" : 1000,
        "localTime" : ISODate("2017-09-25T11:26:37.835Z"),
        "maxWireVersion" : 5,
        "minWireVersion" : 0,
        "readOnly" : false,
        "ok" : 1
    }

    oplogステータスの表示

    rs0:SECONDARY> db.printReplicationInfo()
    configured oplog size:   1214.6957025527954MB
    log length start to end: 862secs (0.24hrs)
    oplog first event time:  Mon Sep 25 2017 19:14:47 GMT+0800 (CST)
    oplog last event time:   Mon Sep 25 2017 19:29:09 GMT+0800 (CST)
    now:                     Mon Sep 25 2017 19:29:15 GMT+0800 (CST)

    機能テスト


    レプリケーション機能


    メインライブラリにデータを挿入します.
    rs0:PRIMARY> use testdb
    switched to db testdb
    rs0:PRIMARY> db.user.insert({'u':'test', 'p':'123'})
    WriteResult({ "nInserted" : 1 })
    rs0:PRIMARY> db.user.find()
    { "_id" : ObjectId("59c8e698c28239e0dfd6f578"), "u" : "test", "p" : "123" }

    ライブラリからログインすると、データが同期していることがわかります.
    $ mongo --port 27018
    rs0:SECONDARY> rs.slaveOk()
    rs0:SECONDARY> show dbs;
    admin   0.000GB
    local   0.000GB
    testdb  0.000GB
    rs0:SECONDARY> use testdb
    switched to db testdb
    rs0:SECONDARY> db.user.find()
    { "_id" : ObjectId("59c8e698c28239e0dfd6f578"), "u" : "test", "p" : "123" }

    じどうフェイルオーバ


    プライマリノードを閉じる
    #  primary
    $ mongo --port 27017
    #   admin  (  admin  )
    rs0:PRIMARY> use admin
    switched to db admin
    rs0:PRIMARY> db.shutdownServer()
    server should be down...

    ノードのステータスの表示
    rs0:PRIMARY> rs.status()
    {
        "set" : "rs0",
        "date" : ISODate("2017-09-25T11:31:22.492Z"),
        "myState" : 1,
        "term" : NumberLong(2),
        "heartbeatIntervalMillis" : NumberLong(2000),
        "optimes" : {
            "lastCommittedOpTime" : {
                "ts" : Timestamp(1506339074, 1),
                "t" : NumberLong(2)
            },
            "appliedOpTime" : {
                "ts" : Timestamp(1506339074, 1),
                "t" : NumberLong(2)
            },
            "durableOpTime" : {
                "ts" : Timestamp(1506339074, 1),
                "t" : NumberLong(2)
            }
        },
        "members" : [
            {
                "_id" : 0,
                "name" : "127.0.0.1:27017",
                "health" : 0,
                "state" : 8,
                "stateStr" : "(not reachable/healthy)",
                "uptime" : 0,
                "optime" : {
                    "ts" : Timestamp(0, 0),
                    "t" : NumberLong(-1)
                },
                "optimeDurable" : {
                    "ts" : Timestamp(0, 0),
                    "t" : NumberLong(-1)
                },
                "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
                "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
                "lastHeartbeat" : ISODate("2017-09-25T11:31:21.532Z"),
                "lastHeartbeatRecv" : ISODate("2017-09-25T11:30:42.934Z"),
                "pingMs" : NumberLong(0),
                "lastHeartbeatMessage" : "Connection refused",
                "configVersion" : -1
            },
            {
                "_id" : 1,
                "name" : "127.0.0.1:27018",
                "health" : 1,
                "state" : 1,
                "stateStr" : "PRIMARY",
                "uptime" : 1057,
                "optime" : {
                    "ts" : Timestamp(1506339074, 1),
                    "t" : NumberLong(2)
                },
                "optimeDate" : ISODate("2017-09-25T11:31:14Z"),
                "infoMessage" : "could not find member to sync from",
                "electionTime" : Timestamp(1506339053, 1),
                "electionDate" : ISODate("2017-09-25T11:30:53Z"),
                "configVersion" : 1,
                "self" : true
            },
            {
                "_id" : 2,
                "name" : "127.0.0.1:27019",
                "health" : 1,
                "state" : 2,
                "stateStr" : "SECONDARY",
                "uptime" : 992,
                "optime" : {
                    "ts" : Timestamp(1506339074, 1),
                    "t" : NumberLong(2)
                },
                "optimeDurable" : {
                    "ts" : Timestamp(1506339074, 1),
                    "t" : NumberLong(2)
                },
                "optimeDate" : ISODate("2017-09-25T11:31:14Z"),
                "optimeDurableDate" : ISODate("2017-09-25T11:31:14Z"),
                "lastHeartbeat" : ISODate("2017-09-25T11:31:21.530Z"),
                "lastHeartbeatRecv" : ISODate("2017-09-25T11:31:21.725Z"),
                "pingMs" : NumberLong(0),
                "syncingTo" : "127.0.0.1:27018",
                "configVersion" : 1
            }
        ],
        "ok" : 1
    }
    127.0.0.1:27017はすでに到達不可能な状態であることがわかる.127.0.0.1:27018は新しいプライマリノードとして選択される.自動フェイルオーバテストに成功しました.27017のインスタンスを再起動すると、127.0.0.1:27017がスレーブノードになることがわかります.

    その他の構成

  • standard通常ノード:参加投票はアクティブノード
  • になる可能性がある
  • passiveコピーノード:投票に参加するが、アクティブノード
  • にはならない.
  • arbiter調停ノード:投票に参加するだけでノードをコピーしなくてもアクティブノード
  • にはならない.
  • Priority 0から1000の間で、0はコピーノードを表し、1から1000は通常ノード
  • である.
  • arbiterOnly:true調停ノード
  • 運用環境


    京東入門級クラウドサーバー、Ubuntu 16.04,Mongo3.4

    リファレンス

  • MongoDBのマスターコピーおよびレプリカセットのreplset構成チュートリアル
  • mongodbレプリカセット(Replica Set)構築
  • mognodbレプリカセットレプリケーション概念理解
  • MongoDB複製セット原理