概要
基本的に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の更新データを受け取ったら終了します。
var dbCur=db.watch()
while (!dbCur.isExhausted()){
if (dbCur.hasNext()){
var doc = dbCur.next()
printjson(doc)
if (doc.operationType == "delete") {
dbCur.close()
break
}
}
}
別の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にする価値はあると思います。