MongoDBでゆるふわDB体験

第4回 MongoDBのレプリケーションを構築してみよう

この記事を読むのに必要な時間:およそ 6.5 分

レプリケーションでの重要項目

本記事の最後として,MongoDBのレプリケーションで重要な項目について説明します。

  1. レプリケーションの仕組み(Oplog)
  2. 書き込み保証
  3. 読み取りスケーリング
  4. 書き込み/読み取りのタグ付けによる制御

レプリケーションの仕組み(Oplog)

Oplogはレプリケーションに必要なデータ操作オペレーションの集合です※3)⁠実態は,レプリケーションに参加している全てのノードにあるlocalというデータベースの中にあるoplog.rsという名前のCapped Collection※4です。クライアントがプライマリへのデータ操作オペレーションを行う度に,そのオペレーションを再現するのに必要な情報を持つエントリが,自動的にプライマリのOplogに追加されます。セカンダリはプライマリのOplogの変更分をコピーし,オペレーションを実行することで同期します。セカンダリは自身が適用した最後のエントリをタイムスタンプで管理しています。セカンダリは一定期間ごとに,プライマリのOplogをチェックし,更新分があればコピーしてオペレーションを実行します。

※3)
MySQLのバイナリログ(binlog)に近いイメージです。
※4)
サイズ制限付きのコレクションで,サイズをオーバーしたら古いドキュメントから削除されます。

Oplogに保存されているもの

前ページで構築したレプリケーション環境を使って,Oplogを実際に見て確認してみましょう。プライマリに接続してデータをinsertした後,localデータベースに接続してoplog.rsをfindします。

$ mongo --port=30000
MongoDB shell version: 2.2.2
connecting to: 127.0.0.1:30000/test

> use mydb
switched to db mydb
> db.logs.insert({x:1,y:1})

> use local
switched to db local
> db.oplog.rs.find().toArray()
[
        {
                "ts" : {
                        "t" : 1357790856000,
                        "i" : 1
                },
                "h" : NumberLong(0),
                "op" : "n",
                "ns" : "",
                "o" : {
                        "msg" : "initiating set"
                }
        },
        {
                "ts" : {
                        "t" : 1357791170000,
                        "i" : 1
                },
                "h" : NumberLong("6904647020561039377"),
                "op" : "i",
                "ns" : "mydb.logs",
                "o" : {
                        "_id" : ObjectId("50ee3fc023dff231bac25b95"),
                        "x" : 1,
                        "y" : 1
                }
        }
]

2つのドキュメントが表示されますが,2つ目がinsertオペレーションのエントリです。1つ目は初期化時に保存されるオペレーション無しのエントリです。oplog.rsの各フィールドに保存されているデータは表6のようになっています。

表6 oplog.rsのフィールド

フィールド名保存されているデータ
tsオペレーションのBSONタイムスタンプ
hオペレーションのユニークなID
opオペレーションの種類。"i" insert,"u" update,"d" delete,"c" dbコマンド,"n" オペレーション無しなどがある
nsネームスペース。データベース.コレクションで表記される。
oオペレーションで使用されたドキュメントのコピー。

Stale問題

プライマリ/セカンダリ間でOplogの同期が追いつかない場合,Stale問題が発生します。前述したようにOplogはCapped Collectionsです。サイズの上限に達すると,古いドキュメントが削除されます。書き込みオペレーションが非常に多いシステムでは,セカンダリがプライマリのOplogを同期する前に,プライマリのOplogが削除される可能性があります。その場合,mongodは以下のエラーメッセージを出して停止します。

replSet error RS102 too stale to catch up

停止してしまったレプリケーションに対する解決策は,一般的には完全に同期し直すことしかありません。Staleを避けるために,書き込みの量に対して十分な大きさのOplogを用意しておくこと必要があり,Oplogのサイジングは重要な設計項目となります。

Oplogのサイジング

OplogはCapped Collectionなので,作成時以外にサイズ変更することができません。デフォルトのOplogのサイズは表7のようになります。

表7 Oplogのデフォルトサイズ

システムデフォルトサイズ
32bit50MB
64bit1GBまたは,ディスクの空き領域の5%
Mac OS X192MB

Staleを避けるために,Oplogのサイジングは非常に重要となります。Oplogのサイズは,mongodの初回起動時にoplogSizeオプション※5で変更可能です。

Oplogの適切なサイズを見積もる方法のひとつに,本番を想定した書き込みテストを実施し,作成されたOplogのサイズを取得する方法があります。1時間程度,本番想定と同程度の書き込みテストを行った後,以下のコマンドでレプリケーションの最新情報を取得します。

> db.getReplicationInfo()

1時間で作成されたOplogのサイズがわかれば,Oplogのサイジングの目安となります。少なくとも8時間のセカンダリのダウンタイムに耐えられるようにしておくことが推奨されています。

※5)
oplogSizeオプションに関しては前のページに解説があります。

書き込み保証

クエリにオプションを指定することによって,書き込み時のリターンを返すレベルを設定し,書き込み保証を実施することができます。書き込み保証は図3および表8に示すとおり,5段階あります。

図3 書き込み保証

図3 書き込み保証

表8 書き込み保証オプション

オプション書き込み保証レベル
デフォルト無し(非同期)
w = 1メモリ
w = 2最低でも1つのサーバにレプリケーション
j:trueジャーナル(ディスク)
w = majorityレプリケーションに参加しているノードの過半数
w = "タグ"タグで指定したノード

たとえば,ジャーナリングを有効にし,最低でも1つのサーバにレプリケーションさせる場合,Rubyでは以下のようにinsertします。

@collection.insert(doc, :safe => {:w => 2, j: => true})

読み取り負荷分散

MongoDBのレプリケーションでは,アプリケーションからプライマリ/セカンダリの,どのノードから読み取り(Read)するかを設定できます。データの一貫性レベルに応じて,クエリごとに設定することが可能です。設定レベルは表9に示すとおり,5つあります。

表9 読み取り設定

設定名意味
PRIMARYPRIMARYノードのみからReadする
PRIMARY PREFERRED可能であればPRIMARYノードからReadする
SECONDARYSECONDARYノードのみからReadする
SECONDARY PREFERRED可能であればSECONDARYノードからReadする
NEARESTレイテンシの小さいノードからReadする

必ず最新のデータが必要な場合には,強い一貫性を保証する"PRIMARY"を,結果整合性で良い場合はそれ以外をレベルに応じて設定可能です図4)⁠

図4 読み取り負荷分散

図4 読み取り負荷分散

Rubyでの設定例は以下のようになります。

@collection.find({:doc => 'foo'}, :read => :primary)    #primaryノードからReadする
@collection.find({:doc => 'foo'}, :read => :secondary)  #secondaryノード群からReadする

書き込み/読み取りのタグ付けによる制御

tagsオプションとgetLastErrorModesを組み合わせることで,書き込み保障や読み取り設定のモードを定義し,書き込み/読み取り先を指定することができます。たとえば,以下のような設定ドキュメントにした場合,少なくとも2つのデータセンターにまたがるレプリケーションを保障するモード"multiDC"を定義することができます。dcタグはデータセンターの場所を表しています。

> config = {
  _id : "multidcrep",
  members : [
    {
      _id  : 0,
      host : "yokohama1.example.com",
      tags : { dc : "Yokohama" }
    },
    {
      _id  : 1,
      host : "yokohama2.example.com",
      tags : { dc : "Yokohama" }
    },
    {
      _id  : 2,
      host : "yokohama3.example.com",
      tags : { dc : "Yokohama" }
    },
    {
      _id  : 3,
      host : "tokyo1.example.com",
      tags : { dc : "Tokyo" }
    },
    {
      _id  : 4,
      host : "osaka1.example.com",
      tags : { dc : "Osaka" }
    }
  ],
  settings : {
    getLastErrorModes : { multiDC : { dc : 2 } }
  }
}

"dc : 2"は,getLastErrorコマンドが成功するために,"dc"タグで指定された2つ以上の異なる値(ここではデータセンターの名称)が必要であることを示します。つまり,異なる2つのデータセンターのノードに書き込まれたことが保証されます。

定義したモード"multiDC"を利用するには,Rubyでは以下のようにします。

@collection.insert(doc, :safe => {:w => "multiDC"})

次回のテーマ

今回はMongoDBが備えているレプリケーション機能について説明しました。MongoDBはスケールアウトを前提に設計されているので,書き込み/読み取りの負荷分散に関する高度な設定も可能です。一方で,書き込み負荷の大きな環境で使用するにはOplogに関する理解が必要ですが,適切に設定を行えば高い可用性を実現できます。

次回はMongoDBのもうひとつのスケールアウトの仕組みであるシャーディングについて説明します。

著者プロフィール

藤崎祥見(ふじさきしょうけん)

野村総合研究所 OpenStandia所属。オープンソースのR&Dとセミナー講師を担当。

Debian,Ubuntu,Liferayのコミュニティで活動した後,MongoDBの翻訳に関わり,丸の内MongoDB勉強会を始める。

実家がお寺で,住職の資格を所持する坊主系エンジニア。

Twitter:@syokenz


渡部徹太郎(わたなべてつたろう)

野村総合研究所 OpenStandia所属。オープンソースを使ったSIやサポートの業務に従事。

藤崎と共同で丸の内MongoDB勉強会を始める。

趣味は自宅サーバ。好きなものはLinuxとRuby。

Twitter:@fetarodc


林田敦(はやしだあつし)

野村総合研究所 OpenStandia所属。オープンソースを使ったSIや製品開発業務に従事。

丸の内MongoDB勉強会では広報兼雑用係を務める。

趣味はレザークラフト,ダイビング,スキー,キャンプ,ジェットスキー,カメラ等々。作って滑って撮って潜れるエンジニア。

Facebook:Atsushi Hayashida

コメント

コメントの記入