MongoDB是一种很不错的NoSQL数据库,数据库存储基于BSON,一种JSON的变种。在使用的时候,有时候我们dump某个单个的collection/db作为备份的时候,往往会连累到整库都访问困难。集群有时候果断是个不错的选择。

MongoDB的存储是Replica,有三种角色如下:

Diagram of a replica set that consists of a primary, a secondary, and an arbiter.

一个cluster中只可以有一个primary,可以有多个secondary,当只有一个primary和一个secondary的时候,需要一个arbiter来帮忙投票哪个才是primary.

目前我们在使用的是tokumx,对外的访问和mongodb是一样的,但内部的配置略有区别。所以若各位使用的是官方mongodb,可能会发现略有不同。

当前,我们已经有一个单个的mongodb node,里面存了一堆数据,我的目标是将此单个node扩展为一个cluster.

首先,我们需要准备一个keyfile.生成方法可以参考这里. 简单说就是两行命令如下:

openssl rand -base64 741 > mongodb-keyfile
chmod 600 mongodb-keyfile

然后就得到了一个mongodb-keyfile. 启动的时候加上keyfile参数如下

/path/to/tokumx/bin/mongod -f xxx.cnf --keyFile /path/to/key/mongodb-keyfile

一般配置文件我都是写在cnf中的,唯独这个keyFile貌似很难找到,所以我直接写启动脚本里面了。

对于单个文件我们并不需要这个keyfile,但若要做cluster,这个似乎是最简单的。因为两个node之间的通讯和验证全靠这个了。

在cnf中记得加上

replSet = your_rs_name

本cluster中所有的replSet都要是这个名字,若对不上也是认证不了的。

然后简单配置下其他环境,primary node就可以启动了。无意中找到一个不错的sample(链接),各位若使用tokumx可以考虑参考下.

启动的脚本和配置文件复制两份,路径稍稍修改下,改个端口启动.这样三个node就启动了.此时三者用的keyfile都是同一个文件。

mongodb Replica 的配置中,默认对机器的识别是hostname:port这么玩的.hostname的查看也很容易

uname -n

一般是写在/etc/hostname中的.内网环境下,你还可以自己写/etc/hosts,强行将名字解析到指定IP中.这样做的好处是之后都可以用一些特定的字符串指代某个机器。当然,非要用ip:port也是可以的。

然后我们就可以进入primary node中开始配置节点了

mongo 127.0.0.1:27017/admin -u your_username -p your_password

进入后,就可以看到提示自己已经在primary界面了。若不是,请到此处复习怎样创建user.顺便一提,tokumx1.5貌似是db.addUser()而不是createUser().

your_rs_name:PRIMARY> rs.initiate()

mongo3.0好像又有点区别, 应该先initiate,然后create user.

> rs.initiate()
xx:PRIMARY> db.createUser({"user":"your_username","pwd":"your_password","roles":["root","dbAdmin","dbOwner"]})

或者 toku 的是:

> rs.initiate()
xx:PRIMARY> db.addUser("your_username","your_password")

初始化结束后,可以查看config

your_rs_name:PRIMARY> rs.conf()
{
     "_id" : "your_rs_name",
     ....
     "members" : [
         {
               "_id":0
               "host" : "your_hostname:your_first_port",
         }
     ]
}

初始状态大致如上. 然后添加arbiter和secondary.

your_rs_name:PRIMARY> rs.addArb("your_arbiter_hostname:arbiter_port")
{ "ok" : 1 }
your_rs_name:PRIMARY> rs.add("your_secondary_hostname:secondary_port")
{ "ok" : 1 }

arbiter节点中并不存储数据,只有一个投票帮助决定primary,而secondary中有完整的备份。 若前面验证都通过了的话,基本应该就加成功了。看log应该可以看到your_hostname:your_first_port正在向your_secondary_hostname:secondary_port同步数据的信息。

这个时候,你的your_hostname:your_first_port这个node和your_secondary_hostname:secondary_port这个node两者的priority是一样的,任何一个都可能是primary node,这可能并不是我们希望的。我们可能希望让某个node为primary node,另一个在第一个node挂了后担当起primary node的作用

your_rs_name:PRIMARY> cfg = rs.config()
{
        "_id" : "your_rs_name",
        "version" : 326971,
        "protocolVersion" : 65,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "your_hostname:your_first_port"
                },
                {
                        "_id" : 1,
                        "host" : "your_arbiter_hostname:arbiter_port",
                        "arbiterOnly" : true
                },
                {
                        "_id" : 2,
                        "host" : "your_secondary_hostname:secondary_port"
                }
        ]
}

我们将当前的config赋值给cfg,然后在cfg上修改一下。

your_rs_name:PRIMARY> cfg.members[0].priority = 1
your_rs_name:PRIMARY> cfg.members[2].priority = 0.5
your_rs_name:PRIMARY> rs.reconfig(cfg, {force: true})

再看config

your_rs_name:PRIMARY> cfg = rs.config()
{
        "_id" : "your_rs_name",
        "version" : 326971,
        "protocolVersion" : 65,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "your_hostname:your_first_port",
                        "priority" : 1
                },
                {
                        "_id" : 1,
                        "host" : "your_arbiter_hostname:arbiter_port",
                        "arbiterOnly" : true
                },
                {
                        "_id" : 2,
                        "host" : "your_secondary_hostname:secondary_port",
                        "priority" : 0.5
                }
        ]
}

当然,你还可以有一些更多的操作.比如添加hide=true可以让外部无法访问,priority=0的时候,这个node永远不可能会是primary.

若某个节点不要了,你可以用remove将其删去

your_rs_name:PRIMARY> rs.remove("your_secondary_hostname:secondary_port")

也可以直接修改config后 force reconfig.

最后,查看当前状态可以使用rs.isMaster()

References

来自的你,很高兴你能看到这儿。若本文对你有所用处,或者内容有什么不足之处,敬请毫不犹豫给个回复。谢谢!