Help us understand the problem. What is going on with this article?

MongoDB スタンドアロンでもReplica Setは有用

概要

基本的にReplica Setは3台以上の構成で使います。しかし、スタンドアロン(1台)でReplica Setとして使うメリットがあります。

Replica Setのメリット

  • トランザクション処理ができる
  • Oplogが使える
    • oplogはoperations logの略で更新ログなどが格納されるCapped Collection
    • localデータベースにoplog.rsのコレクション名で作成される
  • Change Streamsが使える

環境

  • Windows10
  • MongoDB 4.2.0
    • 4.0 以上であれば可能です

Replica Setの設定

サーバーでの設定は--replSetで行います。メモリサイズ以外で注意が必要なのはoplogのサイズです。

Oplogサイズ

OS default 下限値 上限値
Windows 空き容量の5% 990MB 50GB
Unix 空き容量の5% 990MB 50GB
Mac 192MB - -

以降はWindowsを前提に進めます。下限が990MBですが、初期設定で小さなoplogを作ることが出来ます。oplogは再設定でサイズを変更できますが、その時の値は下限値~上限値の内です。ただし、一度990MB以上にすると990MB未満には再設定できません。

次の例はoplogを100MBで作成します。

mongod --dbpath db --replSet repl --oplogSize 100

Replica Setの初期化

mongo
> rs.initiate()
{
        "info2" : "no configuration specified. Using a default configuration for the set",
        "me" : "localhost:27017",
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1568940523, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1568940523, 1)
}
repl:OTHER>
repl:PRIMARY>

初期化が成功してしばらくすると、プロンプトにPRIMARYが出てくるので、それまでenterを押します。
初期化が済むとoplogが作られますので、次にmongodを起動するときは--oplogSizeが無くてもOKです。

次にoplogのサイズの確認とサイズ変更コマンドを紹介します。

repl:PRIMARY> use local
repl:PRIMARY> db.oplog.rs.stats().maxSize
104857600
repl:PRIMARY> db.adminCommand({replSetResizeOplog:1, size: 990}) //990MBに変更

トランザクション処理

トランザクション対象のコレクションはトランザクション処理開始前に存在しなくてはなりませんので、コレクションを作成するところからの例を紹介します。

repl:PRIMARY> db.createCollection("col")
{
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1568958114, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1568958114, 1)
}
repl:PRIMARY> sess = db.getSession()
session { "id" : UUID("1869f6ed-2b47-42fb-9343-7f88f3099a27") }
repl:PRIMARY> sess.startTransaction()
repl:PRIMARY> db.col.insert({a:1})
WriteResult({ "nInserted" : 1 })
repl:PRIMARY> db.col.find()
{ "_id" : ObjectId("5d8466e82ca8580d7e037a0a"), "a" : 1 }
repl:PRIMARY> sess.abortTransaction()
repl:PRIMARY> db.col.find()
repl:PRIMARY>

上記例では、sess.abortTransaction()を実行していますので、insertが取り消されています。更新を確定するためにはsess.commitTransaction()を実行します。

Go言語のトランザクション処理については「MongoDBの Offical Go言語 Driverを使ってみる(1)準備・Insert」の最後に載せています。

Change Streams

Change Streams更新されたデータをリアルタイムで取得する仕組みである。

Change Streamsで取得できるレベルは次の3つあります

  • コレクション
    • 指定したコレクションの変更を取得
    • db.$<$collection$>$.watch()
  • データベース
    • 指定したデータベースの変更を取得
    • db.watch()
  • deployment
    • Replica SetあるいはShardingにおける変更を取得
    • Mongo.watch()

データベースのwatch()例

watch()側のmongoシェルでの処理
次の例は永久ループを回避するためにdeleteの更新データを受け取ったら終了します。

mongoシェル
var dbCur=db.watch()
while (!dbCur.isExhausted()){
   if (dbCur.hasNext()){
      var doc = dbCur.next()
      printjson(doc)
      if (doc.operationType == "delete") {
         dbCur.close()
         break
      }
   } 
}

別のmongoシェルで更新系のコマンドを実行します。

mongoシェル
db.col.insert({a:1,b:1})
db.col.insert({a:1,b:2})
db.col.updateMany({a:1},{$inc:{b:1}})
db.sample.insert({a:1})
db.sample.remove({})

watch()側で表示されるデータ

{
        "_id" : {
                "_data" : "825D8475D0000000012B022C0100296E5A100472FD948260B44F318323BC49331D863446645F696400645D8475D02CA8580D7E037A190004"
        },
        "operationType" : "insert",
        "clusterTime" : Timestamp(1568962000, 1),
        "fullDocument" : {
                "_id" : ObjectId("5d8475d02ca8580d7e037a19"),
                "a" : 1,
                "b" : 1
        },
        "ns" : {
                "db" : "test",
                "coll" : "col"
        },
        "documentKey" : {
                "_id" : ObjectId("5d8475d02ca8580d7e037a19")
        }
}
{
        "_id" : {
                "_data" : "825D8475D0000000022B022C0100296E5A100472FD948260B44F318323BC49331D863446645F696400645D8475D02CA8580D7E037A1A0004"
        },
        "operationType" : "insert",
        "clusterTime" : Timestamp(1568962000, 2),
        "fullDocument" : {
                "_id" : ObjectId("5d8475d02ca8580d7e037a1a"),
                "a" : 1,
                "b" : 2
        },
        "ns" : {
                "db" : "test",
                "coll" : "col"
        },
        "documentKey" : {
                "_id" : ObjectId("5d8475d02ca8580d7e037a1a")
        }
}
{
        "_id" : {
                "_data" : "825D8475D0000000032B022C0100296E5A100472FD948260B44F318323BC49331D863446645F696400645D8475D02CA8580D7E037A190004"
        },
        "operationType" : "update",
        "clusterTime" : Timestamp(1568962000, 3),
        "ns" : {
                "db" : "test",
                "coll" : "col"
        },
        "documentKey" : {
                "_id" : ObjectId("5d8475d02ca8580d7e037a19")
        },
        "updateDescription" : {
                "updatedFields" : {
                        "b" : 2
                },
                "removedFields" : [ ]
        }
}
{
        "_id" : {
                "_data" : "825D8475D0000000042B022C0100296E5A100472FD948260B44F318323BC49331D863446645F696400645D8475D02CA8580D7E037A1A0004"
        },
        "operationType" : "update",
        "clusterTime" : Timestamp(1568962000, 4),
        "ns" : {
                "db" : "test",
                "coll" : "col"
        },
        "documentKey" : {
                "_id" : ObjectId("5d8475d02ca8580d7e037a1a")
        },
        "updateDescription" : {
                "updatedFields" : {
                        "b" : 3
                },
                "removedFields" : [ ]
        }
}
{
        "_id" : {
                "_data" : "825D8475D0000000052B022C0100296E5A1004F505B42981DE4121B97FDE4554C4475E46645F696400645D8475D02CA8580D7E037A1B0004"
        },
        "operationType" : "insert",
        "clusterTime" : Timestamp(1568962000, 5),
        "fullDocument" : {
                "_id" : ObjectId("5d8475d02ca8580d7e037a1b"),
                "a" : 1
        },
        "ns" : {
                "db" : "test",
                "coll" : "sample"
        },
        "documentKey" : {
                "_id" : ObjectId("5d8475d02ca8580d7e037a1b")
        }
}
{
        "_id" : {
                "_data" : "825D8475D1000000012B022C0100296E5A1004F505B42981DE4121B97FDE4554C4475E46645F696400645D8475D02CA8580D7E037A1B0004"
        },
        "operationType" : "delete",
        "clusterTime" : Timestamp(1568962001, 1),
        "ns" : {
                "db" : "test",
                "coll" : "sample"
        },
        "documentKey" : {
                "_id" : ObjectId("5d8475d02ca8580d7e037a1b")
        }
}

データベースに対してwatchしているのでデータベース下のすべてのコレクション(col,sample)に対する変更が取得されています。

まとめ

障害対応を考えるとはやり3台以上の構成が必要ですが、トランザクション処理やChange Streamが使えるので、1台でもReplica Setにする価値はあると思います。 

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away