2. Master/Slave
Master/Slave 是一种典型的备份方案,MongoDB 支持 "One Master Multi Salver" 和 "Multi Master One Slave" 等多种部署方式。
先从简单的 "镜像备份" 开始。
$ sudo mkdir -p /var/mongodb/0
$ sudo mkdir -p /var/mongodb/1
$ sudo ./mongod --fork --logpath /dev/null --dbpath /var/mongodb/0 --master
forked process: 1388
all output going to: /dev/null
$ sudo ./mongod --fork --logpath /dev/null --dbpath /var/mongodb/1 --port 27018 --slave --source localhost:27017 --autoresync
forked process: 1401
all output going to: /dev/null
autoresync 参数会在系统发生意外情况造成主从数据不同步时,自动启动复制操作 (同步复制 10 分钟内仅执行一次)。除此之外,还可以用 --slavedelay 设定更新频率(秒)。
使用 isMaster、printReplicationInfo、printSlaveReplicationInfo 等命令获取相关状态信息。
$ ./mongo
> db.isMaster()
{ "ismaster" : true, "ok" : 1 }
> db.printReplicationInfo()
configured oplog size: 990MB
log length start to end: 1164secs (0.32hrs)
oplog first event time: Mon Aug 23 2010 12:23:54 GMT+0800 (CST)
oplog last event time: Mon Aug 23 2010 12:43:18 GMT+0800 (CST)
now: Mon Aug 23 2010 12:43:24 GMT+0800 (CST)
$ ./mongo localhost:27018
> db.isMaster()
{ "ismaster" : false, "ok" : 1 }
> db.printSlaveReplicationInfo()
source: localhost:27017
syncedTo: Mon Aug 23 2010 12:43:58 GMT+0800 (CST)
= 10secs ago (0hrs)
> show dbs
admin
local
test
> use local
switched to db local
> show collections
me
pair.sync
sources
system.indexes
> db.sources.find()
{ "_id" : ObjectId("4c71f8178d806ad3f54dd89a"), "host" : "localhost:27017", "source" : "main", "syncedTo" : { "t" : 1282538738000, "i" : 1 }, "localLogTs" : { "t" : 0, "i" : 0 } }
Slave 的配置信息保存在 local.sources 中。
通常我们会使用主从方案实现读写分离,但需要设置 Slave_OK。
$ ipython
IPython 0.10 -- An enhanced Interactive Python.
In [1]: from pymongo import *
In [2]: m_conn = Connection()
In [3]: s_conn = Connection(host="localhost:27018", slave_okay=True)
In [4]: m_db = m_conn.test
In [5]: s_db = s_conn.test
# ----------------------------------------- #
In [6]: m_db.users.insert({"name":"user3"})
Out[6]: ObjectId('4c71feb0499b140632000000')
In [7]: s_db.users.find_one({"name":"user3"}) # 数据被复制到 Slave
Out[7]: {u'_id': ObjectId('4c71feb0499b140632000000'), u'name': u'user3'}
In [8]: m_db.users.remove({"name":"user3"}) # 删除 Master 数据
In [9]: for u in m_db.users.find(): print u
....:
{u'_id': ObjectId('4c71fa4d5e01e1ba6d62398f'), u'name': u'user1'}
In [10]: for u in s_db.users.find(): print u # Slave 记录被同步删除
....:
{u'_id': ObjectId('4c71fa4d5e01e1ba6d62398f'), u'name': u'user1'}
# ----------------------------------------- #
In [11]: s_db.users.insert({"name":"userx"}) # 在 Slave 插入数据
Out[11]: ObjectId('4c71ff2c499b140632000001')
In [12]: for u in m_db.users.find(): print u # Master 上无该数据
....:
{u'_id': ObjectId('4c71fa4d5e01e1ba6d62398f'), u'name': u'user1'}
In [13]: for u in s_db.users.find(): print u # Slave 同样没有
....:
{u'_id': ObjectId('4c71fa4d5e01e1ba6d62398f'), u'name': u'user1'}
可见 Slave 读操作正常,但写无效果。
我们还可以部署 "One Slave Two Master" 方案。
$ sudo mkdir -p /var/mongodb/m1
$ sudo mkdir -p /var/mongodb/m2
$ sudo mkdir -p /var/mongodb/s
$ sudo ./mongod --fork --dbpath /var/mongodb/m1 --logpath /dev/null --master
forked process: 1616
all output going to: /dev/null
$ sudo ./mongod --fork --dbpath /var/mongodb/m2 --logpath /dev/null --port 27018 --master
forked process: 1627
all output going to: /dev/null
$ sudo ./mongod --fork --dbpath /var/mongodb/s --logpath /dev/null --port 27019 --slave
forked process: 1638
all output going to: /dev/null
连接 Slave,添加配置。
$ ./mongo localhost:27019
MongoDB shell version: 1.6.1
connecting to: localhost:27019/test
> use local
switched to db local
> db.sources.insert({host:"localhost:27017"})
> db.sources.insert({host:"localhost:27018"})
> db.printSlaveReplicationInfo()
source: localhost:27017
syncedTo: Mon Aug 23 2010 13:06:03 GMT+0800 (CST)
= 17secs ago (0hrs)
source: localhost:27018
doing initial sync
在两台 Master 上添加数据,查看复制效果。
$ ./mongo localhost:27017
MongoDB shell version: 1.6.1
connecting to: localhost:27017/test
> use db1
switched to db db1
> db.users.insert({name:"user1"})
> db.users.insert({name:"user2"})
> db.users.find()
{ "_id" : ObjectId("4c720211ade34e85c5380eab"), "name" : "user1" }
{ "_id" : ObjectId("4c720214ade34e85c5380eac"), "name" : "user2" }
> exit
bye$ ./mongo localhost:27018
MongoDB shell version: 1.6.1
connecting to: localhost:27018/test
> use db2
switched to db db2
> db.blogs.insert({title:"aaa"})
> db.blogs.insert({title:"bbb"})
> db.blogs.insert({title:"ccc"})
> db.blogs.find()
{ "_id" : ObjectId("4c720236f24118cb2f59218d"), "title" : "aaa" }
{ "_id" : ObjectId("4c720239f24118cb2f59218e"), "title" : "bbb" }
{ "_id" : ObjectId("4c72023cf24118cb2f59218f"), "title" : "ccc" }
> exit
bye$ ./mongo localhost:27019
MongoDB shell version: 1.6.1
connecting to: localhost:27019/test
> show dbs
admin
db1
db2
local
> use db1
switched to db db1
> db.users.find()
{ "_id" : ObjectId("4c720211ade34e85c5380eab"), "name" : "user1" }
{ "_id" : ObjectId("4c720214ade34e85c5380eac"), "name" : "user2" }
> use db2
switched to db db2
> db.blogs.find()
{ "_id" : ObjectId("4c720236f24118cb2f59218d"), "title" : "aaa" }
{ "_id" : ObjectId("4c720239f24118cb2f59218e"), "title" : "bbb" }
{ "_id" : ObjectId("4c72023cf24118cb2f59218f"), "title" : "ccc" }
>
一切正常。