MongoDB副本集特性入门指南

549 阅读10分钟

前言

    还在为数据库意外宕机导致系统停用而苦恼吗?还在为数据库压力太大导致系统运行速度比蜗牛还慢而忧愤吗?那就采用DDB(Distributed Database,分布式数据库)吧,DDB真香!言归正传,本文主要介绍MongoDB DDB特性之副本集功能,希望对有需要的小伙伴能有所帮助!

正文

    数据库根据其存储特性可分为RDB(关系型数据库)和NOSQL(非关系型数据库),常见的RDB如oracle、mysql、sql server等,常见的NOSQL如 mongodb、redis、memoryCache等,详细排名见db-engines。从排名中可以看出,RDB中oracle、mysql排名较高,使用群体相近,而NOSQL中mongodb排名最高,使用人数最多。因此,mysql和mongodb也成为新时代码农们的必备技能。

1 准备工作

  • 操作系统:WIN10
  • mongodb官方最新版,安装后自动安装为windows服务,记得配置bin目录到环境变量path下
  • WIN+R 输入services.msc找到MongoDB Server (MongoDB)服务设置启动方式为手动,并关闭服务
  • D盘新建文件夹mongodb,新建1、2、3三个目录,每个目录下新建data 和 log目录以及key_file文件(无后缀名)
  • 使用openssl生成key并粘贴到上述各个key_file文件中,也可以使用加密后的字符串,加密方式随意

2 启动mongodb副本集

    为了方便启动,本文编写了windows批处理脚本。

2.1 编写mongod启动配置文件

    启动配置文件的选项参考官方配置文件说明,编写配置文件的目的是避免写过长的启动脚本,方便修改,先奉上其中data1节点的配置

# data1.cfg

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# Where and how to store data.
storage:
  dbPath: D:\mongodb\1\data
  journal:
    enabled: true
#  engine:
#  wiredTiger:

# where to write logging data.
# systemLog:
#   destination: file
#   logAppend: true
#   path:  D:\mongodb\1\log\mongod.log

# network interfaces
net:
  port: 27017
  bindIp: 0.0.0.0

# repl set
replication:
  replSetName: rs0

# auth
# security:
#  keyFile: D:\mongodb\1\key_file
#  authorization: enabled

注意上边注释了auth, 暂时关闭登陆授权验证以便新建用户

2.2 编写批处理脚本并启动
:: start.bat
start cmd /k mongod -f "./data1.cfg"
start cmd /k mongod -f "./data2.cfg"
start cmd /k mongod -f "./data3.cfg"

cmd转到start.bat所在目录,执行脚本,会出现3个window命令提示符窗口并打印日志,至此3个mongod服务启动成功

2.3 在主节点中添加副本集成员

    WIN+R打开命令提示符窗口输入mongo --port 27017进入mongo客户端。按照如下命令执行即可:

> rs.initiate() //初始化副本集
{
        "info2" : "no configuration specified. Using a default configuration for the set",
        "me" : "hostname:27017",
        "ok" : 1
}
rs0:SECONDARY> rs.add("hostname:27018") //添加副本对象
{
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1631944851, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1631944851, 1)
}
rs0:PRIMARY> rs.add("hostname:27019") //添加副本对象
{
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1631944854, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1631944854, 1)
}
rs0:PRIMARY> rs.conf() //查看副本集配置
{
        "_id" : "rs0",
        "version" : 3,
        "term" : 1,
        "members" : [  //可以看到这里已经有3个副本成员
                {
                        "_id" : 0,
                        "host" : "hostname:27017",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "secondaryDelaySecs" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 1,
                        "host" : "hostname:27018",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "secondaryDelaySecs" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 2,
                        "host" : "hostname:27019",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "secondaryDelaySecs" : NumberLong(0),
                        "votes" : 1
                }
        ],
        ...
}
rs0:PRIMARY> rs.status() // 查看mongodb运行状态
{
        ...
        "members" : [  // 可以看到端口27017节点作为主节点
                {
                        "_id" : 0,
                        "name" : "hostname:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 514,
                        "optime" : {
                                "ts" : Timestamp(1631945197, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2021-09-18T06:06:37Z"),
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "electionTime" : Timestamp(1631944796, 2),
                        "electionDate" : ISODate("2021-09-18T05:59:56Z"),
                        "configVersion" : 5,
                        "configTerm" : 1,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                },
                {
                        "_id" : 1,
                        "name" : "hostname:27018",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 351,
                        "optime" : {
                                "ts" : Timestamp(1631945197, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1631945197, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2021-09-18T06:06:37Z"),
                        "optimeDurableDate" : ISODate("2021-09-18T06:06:37Z"),
                        "lastHeartbeat" : ISODate("2021-09-18T06:06:43.242Z"),
                        "lastHeartbeatRecv" : ISODate("2021-09-18T06:06:41.296Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "luoshenzhou:27017",
                        "syncSourceId" : 0,
                        "infoMessage" : "",
                        "configVersion" : 5,
                        "configTerm" : 1
                },
                {
                        "_id" : 2,
                        "name" : "hostname:27019",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 348,
                        "optime" : {
                                "ts" : Timestamp(1631945197, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1631945197, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2021-09-18T06:06:37Z"),
                        "optimeDurableDate" : ISODate("2021-09-18T06:06:37Z"),
                        "lastHeartbeat" : ISODate("2021-09-18T06:06:43.242Z"),
                        "lastHeartbeatRecv" : ISODate("2021-09-18T06:06:41.817Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "luoshenzhou:27018",
                        "syncSourceId" : 1,
                        "infoMessage" : "",
                        "configVersion" : 5,
                        "configTerm" : 1
                }
        ],
        ...
}
2.4 添加数据库以及用户
rs0:PRIMARY> show dbs //显示所有数据库
admin   0.000GB
config  0.000GB
local   0.000GB
rs0:PRIMARY> use admin //切换到admin数据库
switched to db admin
rs0:PRIMARY> db.createUser({ user: "su", pwd:"123", roles:["root"] }) //添加名为su的超级管理员,后面开启2.1步骤中auth选项后用这个去执行副本集函数,查看副本集
Successfully added user: { "user" : "su", "roles" : [ "root" ] }
rs0:PRIMARY> use test
switched to db test
rs0:PRIMARY> db.createUser({user:"uu",pwd:"123",roles:["dbOwner"]}) // 添加名为uu的数据库成员到test数据库,用这个用户去读写test数据库
Successfully added user: { "user" : "uu", "roles" : [ "dbOwner" ] }
rs0:PRIMARY> show users
{
        "_id" : "test.uu",
        "userId" : UUID("47a7751d-d27b-4537-909f-85e806ce0df1"),
        "user" : "uu",
        "db" : "test",
        "roles" : [
                {
                        "role" : "dbOwner",
                        "db" : "test"
                }
        ],
        "mechanisms" : [
                "SCRAM-SHA-1",
                "SCRAM-SHA-256"
        ]
}
2.5 修改主节点优先级

    这一步就是方便测试用,以免重启后主节点变了

rs0:PRIMARY> config = rs.conf() // 获取配置对象
{
        "_id" : "rs0",
        "version" : 5,
        "term" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "hostname:27017",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "secondaryDelaySecs" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 1,
                        "host" : "hostname:27018",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "secondaryDelaySecs" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 2,
                        "host" : "hostname:27019",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "secondaryDelaySecs" : NumberLong(0),
                        "votes" : 1
                }
        ],
        ...
}
rs0:PRIMARY> config.members[0].priority = 5 // 设置主节点优先级为最高5
rs0:PRIMARY> rs.reconfig(config) // 重载配置
{
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1631946559, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1631946559, 1)
}

3 测试副本集

     副本集测试可以利用可视化工具测试,也可以采用基于nodejs的mvc架构探索一文中express部分案例,本文采用上述文章中的后端代码进行测试。首先关闭所有mongod服务(就是关闭正在运行的命令提示符窗口),开启2.1步骤中auth选项,并重新启动批处理脚本,并启动后端代码,按照如下步骤可以感受副本集DDB特性。

3.1 新增数据
# api.rest
# 批量新增
POST http://localhost:3030/test/addUsers HTTP/1.1
content-type: application/json

 {
     "name": "liming",
     "hobby": "football",
     "num": 10
 }

/*切换到27017节点*/
rs0:PRIMARY> use test
switched to db test
rs0:PRIMARY> db.auth("uu","123")
1
rs0:PRIMARY> db.users.count()
10

/*切换到27018节点,同样新增了10条数据*/
rs0:SECONDARY> rs.secondaryOk()
rs0:SECONDARY> db.users.count()
10
rs0:SECONDARY>

3.2 停用主节点

    直接关闭27017mongod服务所在窗口,然后观察副本集状态

/*切换到27018节点*/
rs0:PRIMARY> use admin
switched to db admin
rs0:PRIMARY> db.auth("su","123")
1
rs0:PRIMARY> rs.status()
{
        ...
        "members" : [ //这里看到主节点已经停止运行, 27018端口节点接替成为新主节点
                {
                        "_id" : 0,
                        "name" : "hostname:27017",
                        "health" : 0,
                        "state" : 8,
                        "stateStr" : "(not reachable/healthy)",
                        "uptime" : 0,
                        "optime" : {
                                "ts" : Timestamp(0, 0),
                                "t" : NumberLong(-1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(0, 0),
                                "t" : NumberLong(-1)
                        },
                        "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
                        "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
                        "lastHeartbeat" : ISODate("2021-09-18T07:55:49.722Z"),
                        "lastHeartbeatRecv" : ISODate("2021-09-18T07:54:53.952Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "Error connecting to hostname:27017 (10.1.13.91:27017) :: caused by :: xxxxx",
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "configVersion" : 6,
                        "configTerm" : 7
                },
                {
                        "_id" : 1,
                        "name" : "hostname:27018",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 3664,
                        "optime" : {
                                "ts" : Timestamp(1631951740, 1),
                                "t" : NumberLong(7)
                        },
                        "optimeDate" : ISODate("2021-09-18T07:55:40Z"),
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "electionTime" : Timestamp(1631951690, 1),
                        "electionDate" : ISODate("2021-09-18T07:54:50Z"),
                        "configVersion" : 6,
                        "configTerm" : 7,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                },
                {
                        "_id" : 2,
                        "name" : "hostname:27019",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 3659,
                        "optime" : {
                                "ts" : Timestamp(1631951740, 1),
                                "t" : NumberLong(7)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1631951740, 1),
                                "t" : NumberLong(7)
                        },
                        "optimeDate" : ISODate("2021-09-18T07:55:40Z"),
                        "optimeDurableDate" : ISODate("2021-09-18T07:55:40Z"),
                        "lastHeartbeat" : ISODate("2021-09-18T07:55:48.617Z"),
                        "lastHeartbeatRecv" : ISODate("2021-09-18T07:55:49.658Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "hostname:27018",
                        "syncSourceId" : 1,
                        "infoMessage" : "",
                        "configVersion" : 6,
                        "configTerm" : 7
                }
        ],
        ...
}
rs0:PRIMARY>
3.3 新增10条数据并恢复主节点

    使用第3.1步配置新增10条数据,重新启用27017mongod服务窗口,然后观察副本集状态

/*切换到27017节点*/
rs0:PRIMARY> use admin
switched to db admin
rs0:PRIMARY> db.auth("su","123")
1
rs0:PRIMARY> rs.status()
{
        ...
        "members" : [ //看到27017重新成为主节点
                {
                        "_id" : 0,
                        "name" : "hostname:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 41,
                        "optime" : {
                                "ts" : Timestamp(1631952101, 1),
                                "t" : NumberLong(8)
                        },
                        "optimeDate" : ISODate("2021-09-18T08:01:41Z"),
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "electionTime" : Timestamp(1631952081, 1),
                        "electionDate" : ISODate("2021-09-18T08:01:21Z"),
                        "configVersion" : 6,
                        "configTerm" : 8,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                },
                {
                        "_id" : 1,
                        "name" : "hostname:27018",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 37,
                        "optime" : {
                                "ts" : Timestamp(1631952101, 1),
                                "t" : NumberLong(8)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1631952101, 1),
                                "t" : NumberLong(8)
                        },
                        "optimeDate" : ISODate("2021-09-18T08:01:41Z"),
                        "optimeDurableDate" : ISODate("2021-09-18T08:01:41Z"),
                        "lastHeartbeat" : ISODate("2021-09-18T08:01:45.484Z"),
                        "lastHeartbeatRecv" : ISODate("2021-09-18T08:01:46.701Z"),
                        "pingMs" : NumberLong(1),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "hostname:27017",
                        "syncSourceId" : 0,
                        "infoMessage" : "",
                        "configVersion" : 6,
                        "configTerm" : 8
                },
                {
                        "_id" : 2,
                        "name" : "hostname:27019",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 37,
                        "optime" : {
                                "ts" : Timestamp(1631952101, 1),
                                "t" : NumberLong(8)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1631952101, 1),
                                "t" : NumberLong(8)
                        },
                        "optimeDate" : ISODate("2021-09-18T08:01:41Z"),
                        "optimeDurableDate" : ISODate("2021-09-18T08:01:41Z"),
                        "lastHeartbeat" : ISODate("2021-09-18T08:01:45.483Z"),
                        "lastHeartbeatRecv" : ISODate("2021-09-18T08:01:45.684Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "hostname:27018",
                        "syncSourceId" : 1,
                        "infoMessage" : "",
                        "configVersion" : 6,
                        "configTerm" : 8
                }
        ],
        ...
}
rs0:PRIMARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB
rs0:PRIMARY> use test
switched to db test
rs0:PRIMARY> db.users.count() //主节点恢复后自动加上了10条数据
20

/*切换到27019节点*/
rs0:SECONDARY> show dbs
rs0:SECONDARY> use test
switched to db test
rs0:SECONDARY> db.auth("uu","123")
1
rs0:SECONDARY> rs.secondaryOk()
rs0:SECONDARY> show tables
users
rs0:SECONDARY> db.users.count()
20
rs0:SECONDARY>

4 可视化工具推荐

    第三步测试工作也可采用可视化工具完成,推荐采用dbeaver企业版进行测试,原因在于dbeaver企业版几乎可以连接市面上所有的RDB和NOSQL,不用频繁切换管理工具!
温馨提醒:dbeaver企业版需要pojie后使用,教程自行度娘

5 国际惯例

欢迎访问原文链接