前言
还在为数据库意外宕机导致系统停用而苦恼吗?还在为数据库压力太大导致系统运行速度比蜗牛还慢而忧愤吗?那就采用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 国际惯例
欢迎访问原文链接