MongoDB复制集
一、MongoDB复制集概述
-
组成
MongoDB复制集(副本集replica set)由一组mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点,MongoDB Driver(客户端)的所有数据都写入primary,secondary通过oplog来同步primary的数据,保证主节点和从节点数据的一致性,复制集在完成主从复制的基础上,通过心跳机制,一旦primary节点出现宕机,则触发选举一个新的主节点,剩下的secondary节点指向新的primary,时间应该在10-30s内完成感知primary节点故障,实现高可用数据库集群; -
特点
- 主是唯一的,但不是固定的
- 通过oplog同步数据,保证数据的一致性;
- 从库无法写入(默认情况下,不使用驱动连接时,读也是不能查询的);
- 相对于传统的主从结构,复制集可以自动容灾;
二、MongoDB复制集原理
-
角色(按是否存储数据划分)
- primary主节点
由选举产生,负责客户端的写操作,产生oplog日志文件 - secondary从节点
负责客户端的读操作,提供数据的备份和故障的切换 - arbiter仲裁节点
只参与选举的投票,不会成为primary,也不向primary同步数据,若部署了一个2个节点的复制集,1个primary,1个secondary,任意节点宕机,复制集将不能提供服务(无法选出primary),这时可以给复制集添加一个arbiter节点,即使有节点宕机,仍能选出primary;
- primary主节点
-
角色(按类型区分)
- standard(标准)
这种是常规节点,它存储一份完整的数据副本,参与投票选举,有可能成为主节点 - passive(被动)
存储完整的数据副本,参与投票,不能成为活跃节点 - arbiter(投票)
仲裁节点只参与投票,不接收复制的数据,也不能成为活跃节点
注:每个参与节点(非仲裁者)有个优先权(0-1000),优先权(priority)为0则是被动的,不能成为活跃节点,优先权不为0的,按照由大到小选出活跃节点,优先值一样的则看谁的数据比较新; 注:Mongodb 3.0里,复制集成员最多50个,参与Primary选举投票的成员最多7个;
- standard(标准)
-
选举
每个节点通过优先级定义出节点的类型(标准、被动、投票);
标准节点通过对比自身数据进行选举出primary节点或者secondary节点;-
影响选举的因素
- 心跳检测
复制集内成员每隔两秒向其他成员发送心跳检测信息,若10秒内无响应,则标记其为不可用 - 连接
在多个节点中,最少保证连个节点为工作状态,如果集群中共三个节点,挂掉两个节点,那么剩余的节点无论状态是primary还是处于选举过程中,都会直接降权为secondary
- 心跳检测
-
触发的选举的情况
- 初始化状况
- 从节点们无法与主节点进行通信
- 主节点辞职
-
主节点辞职情况
- 在接收到replSetStepDown命令后
- 在现有的环境中,其他secondary节点的数据落后与本身10s内,且拥有更高优先级
- 当主节点无法与集群中多数节点通信时
注:当主节点辞职后,主节点将关闭自身所有的连接,避免出现客户端在从节点进行写入操作;
-
-
总结
- 架构方面
复制集由一个primary和多个secondary节点构成,在多个实例中进行选举以上角色; - 选举过程
每个MongoDB实例都会通过优先级被定义成为不同的类型(标准、被动、投票);
标准节点和被动节点进行投票选举,选举一个标准节点作为primary节点;
其他所有节点除仲裁节点以外,都被选举成secondary节点,通过oplog日志文件向primary节点同步数据; - 故障切换
在集群内,所有节点每隔10秒发送一次心跳检测信息,若在规定时间内未响应,则认为宕机,触发选举操作;
在多个节点中,至少两个节点是正常工作状态,如只剩下一个节点,则降级为secondary;
- 架构方面
三、部署MongoDB实现应用复制集
-
异常处理
当primary宕机时,如果有数据未同步到secondary,当primary重新加入时,如果新的primary上已经发生写操作,则旧primary需要回滚部分操作,以保证数据集与新的primary一致;旧primary将会滚的数据写到单独的rollback目录下,数据库管理员可根据需要使用mongorestore进行恢复。 -
环境
系统 | 主机名 | IP | 所需软件 |
---|---|---|---|
centos7.8 | mongodb | 192.168.100.105 | mongodb-linux-x86_64-rhel70-3.6.3.tgz |
- 安装mongodb
[root@mongodb ~]# tar xf mongodb-linux-x86_64-rhel70-3.6.3.tgz
[root@mongodb ~]# mv mongodb-linux-x86_64-rhel70-3.6.3 /usr/local/mongodb
[root@mongodb ~]# echo "export PATH=/usr/local/mongodb/bin:\$PATH" >>/etc/profile
[root@mongodb ~]# source /etc/profile
[root@mongodb ~]# ulimit -n 25000
[root@mongodb ~]# ulimit -u 25000
[root@mongodb ~]# echo 0 >/proc/sys/vm/zone_reclaim_mode
[root@mongodb ~]# sysctl -w vm.zone_reclaim_mode=0
vm.zone_reclaim_mode = 0
[root@mongodb ~]# echo never >/sys/kernel/mm/transparent_hugepage/enabled
[root@mongodb ~]# echo never >/sys/kernel/mm/transparent_hugepage/defrag
- 创建并启动四个实例
[root@mongodb ~]# cd /usr/local/mongodb/bin/
[root@mongodb bin]# mkdir {../mongodb1,../mongodb2,../mongodb3,../mongodb4}
[root@mongodb bin]# mkdir ../logs
[root@mongodb bin]# touch ../logs/mongodb{1..4}.log
[root@mongodb bin]# chmod 777 ../logs/mongodb*
[root@mongodb bin]# cat <<END >>/usr/local/mongodb/bin/mongodb1.conf
bind_ip=192.168.100.105
port=27017
dbpath=/usr/local/mongodb/mongodb1/
logpath=/usr/local/mongodb/logs/mongodb1.log
logappend=true
fork=true
maxConns=5000
replSet=haha
#replication name
END
[root@mongodb bin]# cat <<END >>/usr/local/mongodb/bin/mongodb2.conf
bind_ip=192.168.100.105
port=27018
dbpath=/usr/local/mongodb/mongodb2/
logpath=/usr/local/mongodb/logs/mongodb2.log
logappend=true
fork=true
maxConns=5000
replSet=haha
END
[root@mongodb bin]# vi /usr/local/mongodb/bin/mongodb2.conf
[root@mongodb bin]# cat <<END >>/usr/local/mongodb/bin/mongodb3.conf
bind_ip=192.168.100.105
port=27019
dbpath=/usr/local/mongodb/mongodb3/
logpath=/usr/local/mongodb/logs/mongodb3.log
logappend=true
fork=true
maxConns=5000
replSet=haha
END
[root@mongodb bin]# cat <<END >>/usr/local/mongodb/bin/mongodb4.conf
bind_ip=192.168.100.105
port=27020
dbpath=/usr/local/mongodb/mongodb4/
logpath=/usr/local/mongodb/logs/mongodb4.log
logappend=true
fork=true
maxConns=5000
replSet=haha
END
[root@mongodb bin]# cd
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb1.conf
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb2.conf
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb3.conf
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb4.conf
[root@mongodb ~]# netstat -utpln |grep mongod
tcp 0 0 192.168.100.105:27019 0.0.0.0:* LISTEN 1224/mongod
tcp 0 0 192.168.100.105:27020 0.0.0.0:* LISTEN 1252/mongod
tcp 0 0 192.168.100.105:27017 0.0.0.0:* LISTEN 1168/mongod
tcp 0 0 192.168.100.105:27018 0.0.0.0:* LISTEN 1196/mongod
[root@mongodb ~]# echo -e "/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/mongodb1.conf \n/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/mongodb2.conf\n/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/mongodb3.conf\n/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/mongodb4.conf">>/etc/rc.local
[root@mongodb ~]# chmod +x /etc/rc.local
[root@mongodb ~]# cat <<END >>/etc/init.d/mongodb
#!/bin/bash
INSTANCE=\$1
ACTION=\$2
case "\$ACTION" in
'start')
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/"\$INSTANCE".conf;;
'stop')
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/"\$INSTANCE".conf --shutdown;;
'restart')
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/"\$INSTANCE".conf --shutdown
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/"\$INSTANCE".conf;;
esac
END
[root@mongodb ~]# chmod +x /etc/init.d/mongodb
[root@mongodb ~]# /etc/init.d/mongodb mongodb1 stop
[root@mongodb ~]# /etc/init.d/mongodb mongodb1 start
- 配置实例
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.105
##添加节点
> cfg={"_id":"haha","members":[{"_id":0,"host":"192.168.100.105:27017"},{"_id":1,"host":"192.168.100.105:27018"},{"_id":2,"host":"192.168.100.105:27019"}]}
{
"_id" : "haha",
"members" : [
{
"_id" : 0,
"host" : "192.168.100.105:27017"
},
{
"_id" : 1,
"host" : "192.168.100.105:27018"
},
{
"_id" : 2,
"host" : "192.168.100.105:27019"
}
]
}
##初始化节点
> rs.initiate(cfg)
{
"ok" : 1,
"operationTime" : Timestamp(1661686022, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1661686022, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
##查看复制群状态
haha:OTHER> rs.status()
{
"set" : "haha",
"date" : ISODate("2022-08-28T11:27:19.065Z"),
"myState" : 1,
"term" : NumberLong(1),
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1661686035, 3),
"t" : NumberLong(1)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1661686035, 3),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1661686035, 3),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1661686035, 3),
"t" : NumberLong(1)
}
},
"members" : [
{
"_id" : 0,
"name" : "192.168.100.105:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY", ##主节点
"uptime" : 452,
"optime" : {
"ts" : Timestamp(1661686035, 3),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2022-08-28T11:27:15Z"),
"infoMessage" : "could not find member to sync from",
"electionTime" : Timestamp(1661686033, 1),
"electionDate" : ISODate("2022-08-28T11:27:13Z"),
"configVersion" : 1,
"self" : true
},
{
"_id" : 1,
"name" : "192.168.100.105:27018",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY", ##从节点
"uptime" : 16,
"optime" : {
"ts" : Timestamp(1661686035, 3),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1661686035, 3),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2022-08-28T11:27:15Z"),
"optimeDurableDate" : ISODate("2022-08-28T11:27:15Z"),
"lastHeartbeat" : ISODate("2022-08-28T11:27:17.897Z"),
"lastHeartbeatRecv" : ISODate("2022-08-28T11:27:15.023Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "192.168.100.105:27017",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "192.168.100.105:27019",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY", ##从节点
"uptime" : 16,
"optime" : {
"ts" : Timestamp(1661686035, 3),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1661686035, 3),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2022-08-28T11:27:15Z"),
"optimeDurableDate" : ISODate("2022-08-28T11:27:15Z"),
"lastHeartbeat" : ISODate("2022-08-28T11:27:17.896Z"),
"lastHeartbeatRecv" : ISODate("2022-08-28T11:27:15.018Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "192.168.100.105:27017",
"configVersion" : 1
}
],
"ok" : 1,
"operationTime" : Timestamp(1661686035, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1661686035, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
##添加仲裁节点
haha:PRIMARY> rs.addArb("192.168.100.105:27020")
{
"ok" : 1,
"operationTime" : Timestamp(1661686116, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1661686116, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
haha:PRIMARY> rs.status()
...
{
"_id" : 3,
"name" : "192.168.100.105:27020",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 19,
"lastHeartbeat" : ISODate("2022-08-28T11:28:54.331Z"),
"lastHeartbeatRecv" : ISODate("2022-08-28T11:28:51.329Z"),
"pingMs" : NumberLong(0),
"configVersion" : 2
}
...
注:rs.add()和rs.remove()命令分别用于添加和删除标准节点
haha:PRIMARY> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
haha:PRIMARY> use cloud
switched to db cloud
haha:PRIMARY> db.users.insert({"id":"1","name":"xiaoming"})
WriteResult({ "nInserted" : 1 })
haha:PRIMARY> db.users.find()
{ "_id" : ObjectId("630b53a4c821b079238237d4"), "id" : "1", "name" : "xiaoming" }
haha:PRIMARY> exit
bye
[root@mongodb ~]# mongo --port 27018 --host 192.168.100.105
haha:SECONDARY> show dbs ##secondary节点默认无法读取
2022-08-28T19:39:49.549+0800 E QUERY [thread1] Error: listDatabases failed:{
"operationTime" : Timestamp(1661686785, 1),
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk",
"$clusterTime" : {
"clusterTime" : Timestamp(1661686785, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:65:1
shellHelper.show@src/mongo/shell/utils.js:816:19
shellHelper@src/mongo/shell/utils.js:706:15
#通过以下方式或者驱动方式实现
haha:SECONDARY> db.getMongo().setSlaveOk();
haha:SECONDARY> show dbs
admin 0.000GB
cloud 0.000GB
config 0.000GB
local 0.000GB
haha:SECONDARY> use cloud
switched to db cloud
##secondary节点无法写入
haha:SECONDARY> db.users.insert({"id":"2","name":"xiaohong"})
WriteResult({ "writeError" : { "code" : 10107, "errmsg" : "not master" } })
haha:SECONDARY> db.users.find()
{ "_id" : ObjectId("630b53a4c821b079238237d4"), "id" : "1", "name" : "xiaoming" }
haha:SECONDARY> exit
bye
- 模拟primary故障,查看角色切换情况
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb1.conf --shutdown
[root@mongodb ~]# mongo --port 27018 --host 192.168.100.105
haha:PRIMARY> rs.status()
{
"set" : "haha",
"date" : ISODate("2022-08-28T11:46:53.956Z"),
"myState" : 1,
"term" : NumberLong(2),
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1661687175, 1),
"t" : NumberLong(1)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1661687175, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1661687212, 1),
"t" : NumberLong(2)
},
"durableOpTime" : {
"ts" : Timestamp(1661687212, 1),
"t" : NumberLong(2)
}
},
"members" : [
{
"_id" : 0,
"name" : "192.168.100.105: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("2022-08-28T11:46:53.225Z"),
"lastHeartbeatRecv" : ISODate("2022-08-28T11:46:19.785Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "Connection refused",
"configVersion" : -1
},
{
"_id" : 1,
"name" : "192.168.100.105:27018",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY", ##主节点
"uptime" : 1748,
"optime" : {
"ts" : Timestamp(1661687212, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2022-08-28T11:46:52Z"),
"infoMessage" : "could not find member to sync from",
"electionTime" : Timestamp(1661687191, 1),
"electionDate" : ISODate("2022-08-28T11:46:31Z"),
"configVersion" : 2,
"self" : true
},
{
"_id" : 2,
"name" : "192.168.100.105:27019",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY", ##从节点
"uptime" : 1189,
"optime" : {
"ts" : Timestamp(1661687212, 1),
"t" : NumberLong(2)
},
"optimeDurable" : {
"ts" : Timestamp(1661687212, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2022-08-28T11:46:52Z"),
"optimeDurableDate" : ISODate("2022-08-28T11:46:52Z"),
"lastHeartbeat" : ISODate("2022-08-28T11:46:53.218Z"),
"lastHeartbeatRecv" : ISODate("2022-08-28T11:46:53.380Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "192.168.100.105:27018",
"configVersion" : 2
},
{
"_id" : 3,
"name" : "192.168.100.105:27020",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER", ##仲裁节点
"uptime" : 1097,
"lastHeartbeat" : ISODate("2022-08-28T11:46:53.213Z"),
"lastHeartbeatRecv" : ISODate("2022-08-28T11:46:51.915Z"),
"pingMs" : NumberLong(0),
"configVersion" : 2
}
],
"ok" : 1,
"operationTime" : Timestamp(1661687212, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1661687212, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
haha:PRIMARY> exit
bye
- 启用并手动切换primary角色
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb1.conf
[root@mongodb ~]# mongo --port 27018 --host 192.168.100.105
haha:PRIMARY> cfg={"_id":"haha","members":[{"_id":0,"host":"192.168.100.105:27017","priority":100},{"_id":1,"host":"192.168.100.105:27018","priority":10},{"_id":2,"host":"192.168.100.105:27019","priority":10}]}
haha:PRIMARY> rs.reconfig(cfg)
haha:PRIMARY> exit
bye
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb2.conf --shutdown
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb2.conf
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.105
haha:PRIMARY> exit
bye
- 将标准节点停掉,被动节点也不会成为主节点
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb1.conf --shutdown
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb2.conf --shutdown
[root@mongodb ~]# mongo --port 27019 --host 192.168.100.105
haha:SECONDARY> rs.status()
{
"set" : "haha",
"date" : ISODate("2022-08-28T12:04:41.351Z"),
"myState" : 2,
"term" : NumberLong(4),
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1661688219, 1),
"t" : NumberLong(4)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1661688219, 1),
"t" : NumberLong(4)
},
"appliedOpTime" : {
"ts" : Timestamp(1661688219, 1),
"t" : NumberLong(4)
},
"durableOpTime" : {
"ts" : Timestamp(1661688219, 1),
"t" : NumberLong(4)
}
},
"members" : [
{
"_id" : 0,
"name" : "192.168.100.105: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("2022-08-28T12:04:39.126Z"),
"lastHeartbeatRecv" : ISODate("2022-08-28T12:03:47.988Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "Connection refused",
"configVersion" : -1
},
{
"_id" : 1,
"name" : "192.168.100.105:27018",
"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("2022-08-28T12:04:39.126Z"),
"lastHeartbeatRecv" : ISODate("2022-08-28T12:03:53.292Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "Connection refused",
"configVersion" : -1
},
{
"_id" : 2,
"name" : "192.168.100.105:27019",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 125,
"optime" : {
"ts" : Timestamp(1661688219, 1),
"t" : NumberLong(4)
},
"optimeDate" : ISODate("2022-08-28T12:03:39Z"),
"infoMessage" : "could not find member to sync from",
"configVersion" : 4,
"self" : true
}
],
"ok" : 1,
"operationTime" : Timestamp(1661688219, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1661688219, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
haha:SECONDARY> exit
bye
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb1.conf
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb2.conf
[root@mongodb ~]# netstat -utpln |grep mongod
tcp 0 0 192.168.100.105:27019 0.0.0.0:* LISTEN 1918/mongod
tcp 0 0 192.168.100.105:27020 0.0.0.0:* LISTEN 1252/mongod
tcp 0 0 192.168.100.105:27017 0.0.0.0:* LISTEN 2024/mongod
tcp 0 0 192.168.100.105:27018 0.0.0.0:* LISTEN 2103/mongod
- 查询复制集状态及查看oplog日志文件的大小
[root@mongodb ~]# mongo --port 27019 --host 192.168.100.105
haha:PRIMARY> use local
switched to db local
haha:PRIMARY> rs.printSlaveReplicationInfo() ##查看节点信息
source: 192.168.100.105:27017
syncedTo: Sun Aug 28 2022 20:08:17 GMT+0800 (CST)
0 secs (0 hrs) behind the primary
source: 192.168.100.105:27018
syncedTo: Sun Aug 28 2022 20:08:17 GMT+0800 (CST)
0 secs (0 hrs) behind the primary
haha:PRIMARY> rs.printReplicationInfo()
configured oplog size: 990MB ##oplog大小为990M
log length start to end: 2535secs (0.7hrs)
oplog first event time: Sun Aug 28 2022 19:27:02 GMT+0800 (CST)
oplog last event time: Sun Aug 28 2022 20:09:17 GMT+0800 (CST)
now: Sun Aug 28 2022 20:09:18 GMT+0800 (CST)
haha:PRIMARY> db.oplog.rs.stats()
{
"ns" : "local.oplog.rs",
"size" : 28427,
"count" : 249,
"avgObjSize" : 114,
"storageSize" : 45056,
"capped" : true,
"max" : -1,
"maxSize" : 1038090240, ##单位是字节
...
- 部署用户认证登录(秘钥对)的复制集
[root@mongodb ~]# mongo --port 27019 --host 192.168.100.105
haha:PRIMARY> use admin
switched to db admin
haha:PRIMARY> db.createUser({"user":"root","pwd":"123","roles":["root"]})
Successfully added user: { "user" : "root", "roles" : [ "root" ] }
haha:PRIMARY> exit
bye
[root@mongodb ~]# echo -e "clusterAuthMode=keyFile\nkeyFile=/usr/local/mongodb/bin/cloudkey1" >>/usr/local/mongodb/bin/mongodb1.conf
[root@mongodb ~]# echo -e "clusterAuthMode=keyFile\nkeyFile=/usr/local/mongodb/bin/cloudkey2" >>/usr/local/mongodb/bin/mongodb2.conf
[root@mongodb ~]# echo -e "clusterAuthMode=keyFile\nkeyFile=/usr/local/mongodb/bin/cloudkey3" >>/usr/local/mongodb/bin/mongodb3.conf
[root@mongodb ~]# echo "haha key" >/usr/local/mongodb/bin/cloudkey1
[root@mongodb ~]# echo "haha key" >/usr/local/mongodb/bin/cloudkey2
[root@mongodb ~]# echo "haha key" >/usr/local/mongodb/bin/cloudkey3
[root@mongodb ~]# chmod 600 /usr/local/mongodb/bin/cloudkey*
[root@mongodb ~]# /etc/init.d/mongodb mongodb1 restart
[root@mongodb ~]# /etc/init.d/mongodb mongodb2 restart
[root@mongodb ~]# /etc/init.d/mongodb mongodb3 restart
[root@mongodb ~]# mongo --port 27019 --host 192.168.100.105
haha:PRIMARY> show dbs
2022-08-28T20:15:53.475+0800 E QUERY [thread1] Error: listDatabases failed:{
...
haha:PRIMARY> use admin
switched to db admin
haha:PRIMARY> db.auth("root","123")
1
haha:PRIMARY> show dbs
admin 0.000GB
cloud 0.000GB
config 0.000GB
local 0.000GB
haha:PRIMARY> exit
bye
MongoDB介绍
一、MongoDB
-概述
MongoDB是由C++语言编写的,是一个基于分布式文件存储的非关系型开源数据库系统。其优势在于可以存放海量数据,具备强大的查询功能,是一个独立的面向集合文档形式的
- 起源与发展
2007年10月,MongoDB由10gen团队所发展。2009年2月首度推出。 - 应用平台
MongoDB支持Unix、Linux、Windows等系统平台。
二、MongoDB特点
-
存储型
- 面向集合
Collection-Oriented,数据被分组存储在数据集中,被称为一个集合(Collection)。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库里的表,不同的是它不需要定义任何模式。 - 面向文档
存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各种复杂的文件类型。我们称这种存储形式为BSON(Binary Serialized Document Format); - 高效二进制数据存储
包括大型对象(如视频)。使用二进制格式存储,可以保存任何类型的数据对象;
- 面向集合
-
操作型
- 完全索引
可以在任意属性上建立索引,包含内部对象。以提高查询的速度; - 强大的聚合工具
MongoDB 除了提供丰富的查询功能外,还提供强大的聚合工具,如count、group 等,支持使用MapReduce 完成复杂的聚合任务; - 支持Perl、PHP、Java、C#、JavaScript、Ruby、C和C++语言的驱动程序
MongoDB提供了当前所有主流开发语言的数据库驱动包,开发人员使用任何一种主流开发语言都可以轻松编程,实现访问MongoDB 数据库;
- 完全索引
-
可用性
- 支持复制和数据恢复
MongoDB支持主从复制机制,可以实现数据备份、故障恢复、读扩展等功能。而基于副本集的复制机制提供了自动故障恢复的功能,确保了集群数据不会丢失; - 自动处理分片
MongoDB支持集群自动切分数据,对数据进行分片可以使集群存储更多的数据,实现更大的负载,也能保证存储的负载均衡;
- 支持复制和数据恢复
三、MongoDB应用场景
-
适用场景
- 网站数据
mongo非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性; - 缓存
由于性能很高,mongo也适合作为信息基础设施的缓存层。在系统重启之后,由mongo搭建的持久化缓存可以避免下层的数据源过载; - 密度高、价值数据低
使用传统的关系数据库存储一些数据时可能会比较贵,在此之前,很多程序员往往会选择传统的文件进行存储; - 高伸缩性的场景
mongo非常适合由数十或者数百台服务器组成的数据库。 - 用于对象及JSON数据的存储
mongo的BSON数据格式非常适合文档格式化的存储及查询;
- 网站数据
-
非适用场景
- 高度事务性的系统
例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序; - 传统的商业智能应用
针对特定问题的数据库会对产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择; - 需要传统的SQL语句的场景
- 高度事务性的系统
-
MongoDB案例应用链接
http://www.mongoing.com/archives/3734
四、MongoDB存储结构
-
逻辑结构
- 文档(document)
是mongodb核心概念也是逻辑存储的最小单元,相当于行; - 集合(collection)
多个文档组成集合,相当于表,但不同于表的是无固定结构; - 数据库(database)
多个集合组成数据库 - 总结
一个MongoDB实例可以包含一组数据库,一个DataBase可以包含一组Collection,一个集合可以包含一组Document。一个Document包含一组field(字段),每一个字段都是一个key/value pair;
- 文档(document)
-
物理结构
- .ns文件
每个表或索引对应一个命名空间,数据量增加,文件数量增多,存储了分配和正在使用的磁盘空间; - 数据文件
存放数据的实体,并且使用预分配空间机制; - 日志文件
系统日志文件、journal日志文件(用于mongodb崩溃恢复的保障)、oplog复制操作日志文件(相当于mysql的binary log文件)、慢查询日志:查询操作超出超时时间的慢动作;
注: 在mongodb 3.0版本后,默认的存储引擎已经改变,成为WT(wiredTiger)存储引擎,无以前的.ns文件; wiredTiger引擎:3.0新增引擎,官方宣称在read、insert和复杂的update下具有更高的性能。可以支撑更高的读写负载和并发量。因为对于production环境,更多的CPU可以有效提升wireTiger的性能,因为它是的IO是多线程的。wiredTiger不像MMAPV1引擎那样尽可能的耗尽内存,它可以通过在配置文件中指定“cacheSizeGB”参数设定引擎使用的内存量; journal就是一个预写事务日志,来确保数据的持久性,wiredTiger每隔60秒(默认)或者待写入的数据达到2G时,mongodb将对journal文件提交一个checkpoint(检测点,将内存中的数据变更flush到磁盘中的数据文件中,并做一个标记点,表示此前的数据表示已经持久存储在了数据文件中,此后的数据变更存在于内存和journal日志)。对于write操作,首先被持久写入journal,然后在内存中保存变更数据,条件满足后提交一个新的检测点,即检测点之前的数据只是在journal中持久存储,但并没有在mongodb的数据文件中持久化,延迟持久化可以提升磁盘效率; 为了确保数据的安全性,mongodb将所有的变更操作写入journal并间歇性的持久到磁盘上,对于实际数据文件将延迟写入,和wiredTiger一样journal也是用于数据恢复;
- .ns文件
详看原文https://blog.csdn.net/xiaoql520/article/details/76973887
-
内存映射存储引擎MMAP
- mongodb会把数据文件映射到内存中,如果是读操作,内存中的数据起到缓存的作用,如果是写操作,内存还可以把随机的写操作转换成顺序的写操作,总之可以大幅度提升性能;
- MongoDB并不干涉内存管理工作,而是把这些工作留给操作系统的虚拟内存管理器去处理,这样做的好处是简化了MongoDB的工作,但坏处是你没有方法很方便的控制MongoDB占多大内存,幸运的是虚拟内存管理器的存在让我们多数时候并不需要关心这个问题;
- MongoDB的内存使用机制(持久化)让它在缓存重建方面更有优势,简而言之:如果重启进程,那么缓存依然有效;
五、MongoDB数据类型
六、部署MongoDB
- 系统环境
系统 | 主机名 | IP | 所需软件 |
---|---|---|---|
centos7.8 | mongodb | 192.168.100.108 | mongodb-linux-x86_64-rhel70-3.6.3.tgz |
-
推荐学习地址http://www.runoob.com/mongodb/mongodb-php.html
-
环境部署
[root@mongodb ~]# ulimit -n 25000 ##同一时间最多开启的文件数
[root@mongodb ~]# ulimit -u 25000 ##用户最多开启的程序数目
[root@mongodb ~]# echo 0 >/proc/sys/vm/zone_reclaim_mode ##设置内核参数,当某个内存区域节点内存不足时,可以借用其他区域节点的内存
[root@mongodb ~]# sysctl -w vm.zone_reclaim_mode=0
vm.zone_reclaim_mode = 0
[root@mongodb ~]# echo never >/sys/kernel/mm/transparent_hugepage/enabled ##设置内存分配页
[root@mongodb ~]# echo never >/sys/kernel/mm/transparent_hugepage/defrag
- 解压MongoDB软件包
[root@mongodb ~]# tar xf mongodb-linux-x86_64-rhel70-3.6.3.tgz
[root@mongodb ~]# mv mongodb-linux-x86_64-rhel70-3.6.3 /usr/local/mongodb
[root@mongodb ~]# echo "export PATH=/usr/local/mongodb/bin:\$PATH" >>/etc/profile
[root@mongodb ~]# source /etc/profile
- 创建mongodb存储目录以及配置文件
[root@mongodb ~]# mkdir /usr/local/mongodb/mongodb1
[root@mongodb ~]# mkdir /usr/local/mongodb/logs/
[root@mongodb ~]# touch /usr/local/mongodb/logs/mongodb1.log
[root@mongodb ~]# chmod 777 /usr/local/mongodb/logs/mongodb1.log
[root@mongodb ~]# cat <<END >>/usr/local/mongodb/bin/mongodb1.conf
bind_ip=192.168.100.108
port=27017
dbpath=/usr/local/mongodb/mongodb1/
logpath=/usr/local/mongodb/logs/mongodb1.log
logappend=true
fork=true
maxConns=5000
END
- 编写服务启动脚本
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb1.conf ##启动实例
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108 ##连接服务命令,ctrl+D退出
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb1.conf --shutdown ##关闭实例
[root@mongodb ~]# echo /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/mongodb1.conf >>/etc/rc.local ##设置开机启动
[root@mongodb ~]# chmod +x /etc/rc.local
[root@mongodb ~]# cat <<END >>/etc/init.d/mongodb
#!/bin/bash
INSTANCE=\$1
ACTION=\$2
case "\$ACTION" in
'start')
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/"\$INSTANCE".conf;;
'stop')
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/"\$INSTANCE".conf --shutdown;;
'restart')
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/"\$INSTANCE".conf --shutdown
/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/"\$INSTANCE".conf;;
esac
END
[root@mongodb ~]# chmod +x /etc/init.d/mongodb
[root@mongodb ~]# /etc/init.d/mongodb mongodb1 start ##启动
[root@mongodb ~]# /etc/init.d/mongodb mongodb1 stop ##停止
[root@mongodb ~]# /etc/init.d/mongodb mongodb1 restart ##重启
- 测试访问MongoDB数据库
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108
MongoDB shell version v3.6.3
connecting to: mongodb://192.168.100.108:27017/
MongoDB server version: 3.6.3
Server has startup warnings:
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten]
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten]
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten]
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten]
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2022-08-28T11:51:49.272+0800 I CONTROL [initandlisten]
> show dbs
admin 0.000GB
local 0.000GB
> exit
bye
七、MongoDB中语句操作
##查看帮助
> help
> db.help
##查看数据库
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
> show databases
admin 0.000GB
config 0.000GB
local 0.000GB
##进入数据库,没有且创建
> use local
switched to db local
##查看集合
> show tables
startup_log
> show collections
startup_log
##创建linux数据库
> use linux
switched to db linux
##创建集合user
> db.user.insert({"id":1,"name":"tom"});
WriteResult({ "nInserted" : 1 })
##查看数据库
> show dbs
admin 0.000GB
config 0.000GB
linux 0.000GB
local 0.000GB
##查看集合
> show collections
user
##查看集合中的文档
> db.user.find()
{ "_id" : ObjectId("630ae8a21abdef07d14191fc"), "id" : 1, "name" : "tom" }
##统计集合的文档条目
> db.user.count()
1
##查看数据库状态
> db.stats()
{
"db" : "linux",
"collections" : 1,
"views" : 0,
"objects" : 1,
"avgObjSize" : 48,
"dataSize" : 48,
"storageSize" : 16384,
"numExtents" : 0,
"indexes" : 1,
"indexSize" : 16384,
"fsUsedSize" : 2132975616,
"fsTotalSize" : 18238930944,
"ok" : 1
}
##集合中插入文档条目,并设置多种数据类型
> db.user.insert({"id":2,"name":"jack","isadmin":true,"gender":null,"favorite":["apple","banana" ,"orange",1,2,3],"regtime":new Date()});
WriteResult({ "nInserted" : 1 })
##查看集合中文档内容
> db.user.find()
{ "_id" : ObjectId("630ae8a21abdef07d14191fc"), "id" : 1, "name" : "tom" }
{ "_id" : ObjectId("630ae8f31abdef07d14191fd"), "id" : 2, "name" : "jack", "isadmin" : true, "gender" : null, "favorite" : [ "apple", "banana", "orange", 1, 2, 3 ], "regtime" : ISODate("2022-08-28T04:02:59.141Z") }
##根据key值搜索集合中文档内容
> db.user.findOne({"id":2})
{
"_id" : ObjectId("630ae8f31abdef07d14191fd"),
"id" : 2,
"name" : "jack",
"isadmin" : true,
"gender" : null,
"favorite" : [
"apple",
"banana",
"orange",
1,
2,
3
],
"regtime" : ISODate("2022-08-28T04:02:59.141Z")
}
##查看集合中值的类型
> a=db.user.findOne({'id':2})
{
"_id" : ObjectId("630ae8f31abdef07d14191fd"),
"id" : 2,
"name" : "jack",
"isadmin" : true,
"gender" : null,
"favorite" : [
"apple",
"banana",
"orange",
1,
2,
3
],
"regtime" : ISODate("2022-08-28T04:02:59.141Z")
}
##查看id值类型为数字
> typeof(a.id)
number
##查看name值类型为字符串
> typeof(a.name)
string
##后插入的key值会覆盖先存在的key值
> db.user.insert({"id":1,"id":2});
WriteResult({ "nInserted" : 1 })
> db.user.find()
...
{ "_id" : ObjectId("630aeb4d1abdef07d14191fe"), "id" : 2 }
##默认区分大小写
> db.user.insert({"id":1,"ID":2});
WriteResult({ "nInserted" : 1 })
> db.user.find()
...
{ "_id" : ObjectId("630aeb4d1abdef07d14191fe"), "id" : 2 }
{ "_id" : ObjectId("630aebca1abdef07d14191ff"), "id" : 1, "ID" : 2 }
##key值可以为kong,但是通常不会用
> db.user.insert({"":"20"});
WriteResult({ "nInserted" : 1 })
> db.user.find()
...
{ "_id" : ObjectId("630aec101abdef07d1419200"), "" : "20" }
##key值不可以是$开头
> db.user.insert({"$id":"20"});
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 2,
"errmsg" : "Document can't have $ prefixed field names: $id"
}
})
##特殊符号可以作为key值中的一部分
> db.user.insert({"i$d":"20"});
WriteResult({ "nInserted" : 1 })
> db.user.find()
...
{ "_id" : ObjectId("630aecaf1abdef07d1419202"), "i$d" : "20" }
##更新集合中文档的内容
> db.user.update({"id":1},{$set:{"name":"xiaohong"}});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find()
{ "_id" : ObjectId("630ae8a21abdef07d14191fc"), "id" : 1, "name" : "xiaohong" }
##批量增加文档条目
> for(i=1;i<=1000;i++){db.user.insert({"id":i,"name":"haha"})};
WriteResult({ "nInserted" : 1 })
> db.user.find()
...
{ "_id" : ObjectId("630aed901abdef07d1419203"), "id" : 1, "name" : "haha" }
{ "_id" : ObjectId("630aed901abdef07d1419204"), "id" : 2, "name" : "haha" }
{ "_id" : ObjectId("630aed901abdef07d1419205"), "id" : 3, "name" : "haha" }
{ "_id" : ObjectId("630aed901abdef07d1419206"), "id" : 4, "name" : "haha" }
{ "_id" : ObjectId("630aed901abdef07d1419207"), "id" : 5, "name" : "haha" }
...
##删除集合中的某条文档
> db.user.remove({"id":1})
WriteResult({ "nRemoved" : 3 })
##删除集合
> db.user.drop()
true
##删除当前所在的数据库
> db.dropDatabase()
{ "dropped" : "linux", "ok" : 1 }
##查看mongodb版本
> db.version()
3.6.3
##退出
> ctrl+d
八、MongoDB数据库的导入导出、备份恢复、复制数据库
- 导入导出语法
导入语法:
mongoimport -d database_name -c collection_name --file source_name
导出语法:
mongoexport -d database_name -c coolection_name [-f list_name] -o backup.jso
##有些选项为可选,若进行数据过滤条件判断指定选项-q
- 案例
[root@mongodb ~]# yum -y install mysql mariadb-server
[root@mongodb ~]# systemctl start mariadb
[root@mongodb ~]# mysql
MariaDB [(none)]> create database linux;
MariaDB [(none)]> use linux;
MariaDB [linux]> create table t1(id int,name varchar(20));
MariaDB [linux]> insert into t1 values(1,'jack');
Query OK, 1 row affected (0.00 sec)
MariaDB [linux]> insert into t1 values(2,'Rose');
Query OK, 1 row affected (0.01 sec)
MariaDB [linux]> select * from t1;
+------+------+
| id | name |
+------+------+
| 1 | jack |
| 2 | Rose |
+------+------+
2 rows in set (0.00 sec)
MariaDB [linux]> select * from linux.t1 into outfile '/tmp/t1_mysql.csv' fields terminated by ",";
Query OK, 2 rows affected (0.00 sec)
MariaDB [linux]> exit
Bye
[root@mongodb ~]# cat /tmp/systemd-private-b015b893b70a47d2bc3d3f2b7993359a-mariadb.service-JXIqTg/tmp/t1_mysql.csv
1,jack
2,Rose
[root@mongodb ~]# /usr/local/mongodb/bin/mongoimport --port 27017 --host 192.168.100.108 -d benet -c tt1 -f id,name --file /tmp/systemd-private-b015b893b70a47d2bc3d3f2b7993359a-mariadb.service-JXIqTg/tmp/t1_mysql.csv --type csv
2022-08-28T12:40:35.413+0800 connected to: 192.168.100.108:27017
2022-08-28T12:40:35.425+0800 imported 2 documents
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108
> show dbs
admin 0.000GB
benet 0.000GB
config 0.000GB
local 0.000GB
> use benet
switched to db benet
> show tables;
tt1
> db.tt1.find()
{ "_id" : ObjectId("630af1c3501853411e392cec"), "id" : 1, "name" : "jack" }
{ "_id" : ObjectId("630af1c3501853411e392ced"), "id" : 2, "name" : "Rose" }
> for(var i=1;i<=10000;i++)db.tt1.insert({"id":i,"name":"name"+i});
WriteResult({ "nInserted" : 1 })
> db.tt1.find({"id":{"$gt":5000}}).limit(3);
{ "_id" : ObjectId("630af278db5771d52ba03b8a"), "id" : 5001, "name" : "name5001" }
{ "_id" : ObjectId("630af278db5771d52ba03b8b"), "id" : 5002, "name" : "name5002" }
{ "_id" : ObjectId("630af278db5771d52ba03b8c"), "id" : 5003, "name" : "name5003" }
> db.tt1.find({"id":{"$gt":5000}}).count();
5000
> exit
bye
[root@mongodb ~]# /usr/local/mongodb/bin/mongoexport --host 192.168.100.108 --port 27017 -d benet -c tt1 -q '{"id":{"$gt":5000}}' -o /tmp/test.json
2022-08-28T12:47:53.190+0800 connected to: 192.168.100.108:27017
2022-08-28T12:47:53.271+0800 exported 5000 records
[root@mongodb ~]# ls /tmp/
mongodb-27017.sock systemd-private-b015b893b70a47d2bc3d3f2b7993359a-vgauthd.service-ThP1Bx
systemd-private-b015b893b70a47d2bc3d3f2b7993359a-chronyd.service-9Nf59b systemd-private-b015b893b70a47d2bc3d3f2b7993359a-vmtoolsd.service-RM8UIQ
systemd-private-b015b893b70a47d2bc3d3f2b7993359a-mariadb.service-JXIqTg test.json
[root@mongodb ~]# wc -l /tmp/test.json
5000 /tmp/test.json
- 备份恢复
逻辑备份
mongodump -h server_ip -d database_name -o dbdirectory
物理备份:冷备
恢复
mongorestore -d database_name --dir=dbdirectory
- 案例
[root@mongodb ~]# mkdir /backup
[root@mongodb ~]# /usr/local/mongodb/bin/mongodump --host 192.168.100.108 --port 27017 -d benet -o /backup/
2022-08-28T12:52:22.805+0800 writing benet.tt1 to
2022-08-28T12:52:22.881+0800 done dumping benet.tt1 (10002 documents)
[root@mongodb ~]# ls /backup/benet/
tt1.bson tt1.metadata.json
注解:bson是由10gen开发的一个数据格式,目前主要用于mongoDB中,是mongoDB的数据存储格式。bson基于json格式,选择json进行改造的原因主要是json的通用性及json的schemaless的特性;
[root@mongodb ~]# /usr/local/mongodb/bin/mongorestore --host 192.168.100.108 --port 27017 -d bdqn --dir=/backup/benet/
2022-08-28T12:54:23.974+0800 the --db and --collection args should only be used when restoring from a BSON file. Other uses are deprecated and will not exist in the future; use --nsInclude instead
2022-08-28T12:54:23.975+0800 building a list of collections to restore from /backup/benet dir
2022-08-28T12:54:23.976+0800 reading metadata for bdqn.tt1 from /backup/benet/tt1.metadata.json
2022-08-28T12:54:23.986+0800 restoring bdqn.tt1 from /backup/benet/tt1.bson
2022-08-28T12:54:24.119+0800 no indexes to restore
2022-08-28T12:54:24.119+0800 finished restoring bdqn.tt1 (10002 documents)
2022-08-28T12:54:24.119+0800 done
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108
> show dbs
admin 0.000GB
bdqn 0.000GB
benet 0.000GB
config 0.000GB
local 0.000GB
> exit
- 复制数据库
复制数据库:
db.copyDatabase("source_db","des_db","192.168.100.108")
克隆集合:
db.runCommand({cloneCollection:"accp.t1",from:"192.168.100.102:27017"})
##将远程ip服务器的某个数据库中的某个集合克隆到本地
- 案例
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108
> db.copyDatabase("bdqn","bdqn2","192.168.100.108");
{ "ok" : 1 }
> show dbs
admin 0.000GB
bdqn 0.000GB
bdqn2 0.000GB
benet 0.000GB
config 0.000GB
local 0.000GB
> exit
bye
九、MongoDB数据库的用户角色权限管理
-
注意
- 创建用户时
需要在默认test数据库中,不use到任何数据库,不然会导致普通用户的归属数据库错乱; - 用户在验证身份登录时
必须要use到本用户的所属db数据库; - 查看扫描用户时
需要在admin数据库中,执行use admin切换进去; - 撤销修改其权限时
需要在用户所属的db数据库中进行; - 管理员删除用户时
必须在admin数据库中执行删除;
- 创建用户时
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108 ##登录后默认在test数据库中
##查看当前用户
> db.getUsers()
[ ]
##创建超级管理员root
> db.createUser({user:"root",pwd:"abc123",roles:[{role:"root",db:"admin"}]})
Successfully added user: {
"user" : "root",
"roles" : [
{
"role" : "root",
"db" : "admin"
}
]
}
##查看当前用户
> db.getUsers()
[
{
"_id" : "test.root",
"user" : "root",
"db" : "test", ##所属数据库
"roles" : [
{
"role" : "root",
"db" : "admin" ##拥有权限的管理数据库
}
]
}
]
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb1.conf --shutdown
[root@mongodb ~]# mongod -f /usr/local/mongodb/bin/mongodb1.conf --auth
about to fork child process, waiting until server is ready for connections.
forked process: 15675
child process started successfully, parent exiting
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108
> show dbs ##无法认证,所以报错
2022-08-28T13:09:12.646+0800 E QUERY [thread1] Error: listDatabases failed:{
"ok" : 0,
"errmsg" : "not authorized on admin to execute command { listDatabases: 1.0, $db: \"admin\" }",
"code" : 13,
"codeName" : "Unauthorized"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:65:1
shellHelper.show@src/mongo/shell/utils.js:816:19
shellHelper@src/mongo/shell/utils.js:706:15
@(shellhelp2):1:1
> db.auth('root','abc123') ##认证用户
1
> show dbs ##验证查看权限
admin 0.000GB
bdqn 0.000GB
bdqn2 0.000GB
benet 0.000GB
config 0.000GB
local 0.000GB
> db.haha.insert({"id":"1","num":"101"}) ##测试插入内容
WriteResult({ "nInserted" : 1 })
> db.haha.find()
{ "_id" : ObjectId("630afa790f394408576d6329"), "id" : "1", "num" : "101" }
##创建普通用户,注意,当前root所在的数据库是test,所以创建的用户hahaadmin所属库也是test,也就是说hahaadmin在登陆时只能在test库中才能认证成功
> db.createUser({user:"hahaadmin",pwd:"123123",roles:[{role:"read",db:"haha"},{role:"readWrite",db:"hehe"}]})
Successfully added user: {
"user" : "hahaadmin",
"roles" : [
{
"role" : "read",
"db" : "haha"
},
{
"role" : "readWrite",
"db" : "hehe"
}
]
}
##查看当前用户
> db.getUsers()
[
{
"_id" : "test.hahaadmin",
"user" : "hahaadmin",
"db" : "test",
"roles" : [
{
"role" : "read",
"db" : "haha"
},
{
"role" : "readWrite",
"db" : "hehe"
}
]
},
{
"_id" : "test.root",
"user" : "root",
"db" : "test", ##所属库
"roles" : [
{
"role" : "root",
"db" : "admin"
}
]
}
]
##切换到admin数据库内,扫描所有的mongodb内的用户
> use admin
switched to db admin
> db.system.users.find()
{ "_id" : "test.root", "user" : "root", "db" : "test", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "v2dJWCCErnbPoPVMbnuqFA==", "storedKey" : "hxhDLSqDRUeC8BO/qSkWRjtwpTE=", "serverKey" : "539frIWAAuJtwZQxzWOPOxcdYis=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
{ "_id" : "test.hahaadmin", "user" : "hahaadmin", "db" : "test", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "yyOYhCn4P4tQ7eim9Or+AQ==", "storedKey" : "4P5NDvPLNJ1Cs9w+9tesArLfOjA=", "serverKey" : "pWR/ALlYFggx517MKyn5XmdniZw=" } }, "roles" : [ { "role" : "read", "db" : "haha" }, { "role" : "readWrite", "db" : "hehe" } ] }
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108
##登陆普通用户,进行认证
> db.auth('hahaadmin','123123')
1
##切换至有读权限的数据库,测试能够读取数据,但是无法写入数据
> use haha
switched to db haha
> db.user.insert({"id":1,"name":"zs"})
WriteResult({
"writeError" : {
"code" : 13,
"errmsg" : "not authorized on haha to execute command { insert: \"user\", ordered: true, $db: \"haha\" }"
}
})
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108
##登陆普通用户进行认证
> db.auth('hahaadmin','123123')
1
##切换至有读写权限的数据库,测试能够读取数据,也可以写入数据
> use hehe
switched to db hehe
> db.user.insert({"id":1,"name":"ls"})
WriteResult({ "nInserted" : 1 })
> db.user.find()
{ "_id" : ObjectId("630afda9e8160ce0488d2ff6"), "id" : 1, "name" : "ls" }
[root@mongodb ~]# mongo --port 27017 --host 192.168.100.108
> db.auth('root','abc123')
1
##为hahaadmin用户提升在haha数据库中的权限,需要首先切换到hahaadmin用户所在的所属数据库test中
> use test
switched to db test
##现有权限基础上加权限
> db.grantRolesToUser("hahaadmin",[{role:"readWrite",db:"haha"}])
> use admin
switched to db admin
##扫描所有用户及权限
> db.system.users.find()
{ "_id" : "test.root", "user" : "root", "db" : "test", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "v2dJWCCErnbPoPVMbnuqFA==", "storedKey" : "hxhDLSqDRUeC8BO/qSkWRjtwpTE=", "serverKey" : "539frIWAAuJtwZQxzWOPOxcdYis=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
{ "_id" : "test.hahaadmin", "user" : "hahaadmin", "db" : "test", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "yyOYhCn4P4tQ7eim9Or+AQ==", "storedKey" : "4P5NDvPLNJ1Cs9w+9tesArLfOjA=", "serverKey" : "pWR/ALlYFggx517MKyn5XmdniZw=" } }, "roles" : [ { "role" : "readWrite", "db" : "haha" }, { "role" : "read", "db" : "haha" }, { "role" : "readWrite", "db" : "hehe" } ] }
> use test
switched to db test
##现有权限基础上撤销权限
> db.revokeRolesFromUser("hahaadmin",[{role:"readWrite",db:"hehe"}])
> use admin
switched to db admin
##扫描所有用户及权限
> db.system.users.find()
{ "_id" : "test.root", "user" : "root", "db" : "test", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "v2dJWCCErnbPoPVMbnuqFA==", "storedKey" : "hxhDLSqDRUeC8BO/qSkWRjtwpTE=", "serverKey" : "539frIWAAuJtwZQxzWOPOxcdYis=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
{ "_id" : "test.hahaadmin", "user" : "hahaadmin", "db" : "test", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "yyOYhCn4P4tQ7eim9Or+AQ==", "storedKey" : "4P5NDvPLNJ1Cs9w+9tesArLfOjA=", "serverKey" : "pWR/ALlYFggx517MKyn5XmdniZw=" } }, "roles" : [ { "role" : "readWrite", "db" : "haha" }, { "role" : "read", "db" : "haha" } ] }
##删除用户
> db.system.users.remove({user:"hahaadmin"})
WriteResult({ "nRemoved" : 1 })
##扫描系统中所有用户,主义当前必须在admin中
> db.system.users.find()
{ "_id" : "test.root", "user" : "root", "db" : "test", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "v2dJWCCErnbPoPVMbnuqFA==", "storedKey" : "hxhDLSqDRUeC8BO/qSkWRjtwpTE=", "serverKey" : "539frIWAAuJtwZQxzWOPOxcdYis=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] }
>
十、MongoDB连接php
- 安装部署lamp及MongoDB驱动
[root@mongodb ~]# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
[root@mongodb ~]# yum -y install httpd php php-devel openssl openssl-devel
[root@mongodb ~]# unzip mongo-php-driver-legacy-master.zip
[root@mongodb ~]# cd mongo-php-driver-legacy-master/
[root@mongodb mongo-php-driver-legacy-master]# phpize
[root@mongodb mongo-php-driver-legacy-master]# ./configure && make && make install
[root@mongodb mongo-php-driver-legacy-master]# cd
[root@mongodb ~]# cat <<END >>/etc/php.ini
[MongoDB]
extension=mongo.so
END
[root@mongodb ~]# systemctl start httpd
测试phpinfo页面
[root@mongodb ~]# cat <<END >>/var/www/html/index.php
<?php
phpinfo();
?>
END
- 测试php连接mongodb数据库
[root@mongodb ~]# vi /var/www/html/a.php
<?php
$m = new MongoClient("mongodb://192.168.100.108:27017");
$db = $m ->linux;
//指定数据库位置,若不存在,则会自动创建
echo "sucess"
?>
- 测试php连接MongoDB数据库并创建集合
[root@mongodb ~]# vi /var/www/html/b.php
<?php
$m = new MongoClient("mongodb://192.168.100.108:27017");
$db = $m->linux;
$collection = $db->createCollection("test1");
echo "集合test1创建成功";
?>
- 测试php连接MongoDB数据库并插入文档
[root@mongodb ~]# vi /var/www/html/c.php
<?php
$m = new MongoClient("mongodb://192.168.100.108:27017");
$db = $m->linux;
$collection = $db->test1;
$document = array(
"title" => "MongoDB",
"description" => "database",
"likes" => 100,
"url" => "http://www.linuxfan.cn","by","linuxfan"
);
$collection->insert($document);
echo "数据插入成功";
?>
- 测试MongoDB数据库内数据
[root@mongodb ~]# mongo --host 192.168.100.108 --port 27017
> show dbs
admin 0.000GB
bdqn 0.000GB
bdqn2 0.000GB
benet 0.000GB
config 0.000GB
haha 0.000GB
hehe 0.000GB
linux 0.000GB
local 0.000GB
> use linux
switched to db linux
> db.test1.find()
{ "_id" : ObjectId("630b014e4b0139904c8b4567"), "title" : "MongoDB", "description" : "database", "likes" : NumberLong(100), "url" : "http://www.linuxfan.cn", "0" : "by", "1" : "linuxfan" }
> exit
bye