通过上一章博客的学习,基本上明白了mongodb的基础和操作,接下来我们来操作mongodb的高级部分
搭建高可用集群:
MongoDB主从复制架构原理和缺陷:
在主从结构中,主节点的操作记录成为oplog(operation log)
oplog存储在一个系统数据库local的集合oplog.$main(递增的加入,但他却是固定的集合)中
这个集合的每个文档都代表主节点上执行的一个操作
从服务器会定期从主服务器中获取oplog记录,然后在本机上执行,从而实现同步(基本上,主从同步都是类似于这样的思想)
对于存储oplog的集合,MongoDB采用的是固定集合,也就是说随着操作过多,新的操作会覆盖旧的操作
mongodb支持传统的master-slave(主-从)架构,master节点负责数据的读写,slave没有写入权限
由于Mongo没有自动故障转移功能(因为是传统的主从架构)
redis和zookeeper都有选举的操作(在传统的基础上进行扩展的),而这里没有,所以主库没了,是会有大问题的,基本写入不了了
即这时就需要我们去手动指定master和slave端,所以不推荐在生产中使用
即在mongodb4.0后不再支持主从复制
官方的说明:[main] Master/slave replication is no longer supported
复制集 replica sets :
什么是复制集(副本集,可以说是集群) :
MongoDB 的复制集不同于以往的主从模式
在集群Master故障的时候,副本集可以自动投票,选举出新的Master
并引导其余的Slave服务器连接新的Master,而这个过程对于应用是透明的(redis和zookeeper也是类似于这样的思想)
可以说MongoDB的复制集是自带故障转移功能的主从复制
相对于传统主从模式是有优势的(也可以说是扩展的),传统的主从模式,需要手工指定集群中的 Master
如果 Master 发生故障,一般都是人工介入,指定新的 Master,这个过程对于应用一般不是透明的
往往伴随着应用重新修改配置文件,重启应用服务器等
而 MongoDB 副本集,集群中的任何节点都可能成为 Master 节点,从而实现与redis和zookeeper的自动转移的操作(选举)
为什么要使用复制集:
高可用:
防止设备(服务器、网络)故障
提供自动 failover(故障转移)功能,通过该技术来保证高可用
灾难恢复:
当发生故障时,可以从其他节点恢复 用于备份
功能隔离:
我们可以在备节点上执行,减少主节点的压力
比如:用于分析、报表,数据挖掘,系统任务等
复制集集群架构原理:
一个副本集即为服务于同一数据集(集群)的多个 MongoDB 实例,其中一个为主节点,其余的都为从节点
主节点上能够完成读写操作,从节点仅能用于读操作
主节点需要记录所有改变数据库状态的操作,这些记录保存在 oplog 中
这个文件存储在 local 数据库,各个从节点通过此 oplog 来复制数据并应用于本地,保持本地的数据与主节点的一致
oplog 具有幂等性(幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的)
即无论执行几次其结果一致,这个比 mysql 的二进制日志更好用
集群中的各节点还会通过传递心跳信息来检测各自的健康状况
当主节点故障时,多个从节点会触发一次新的选举操作
并选举其中的一个成为新的主节点
通常谁的优先级更高,谁就是新的主节点,他并不是完全按照节点票数(一般都是1)的先后的
该优先级参与票数,所以优先级越高,票数也就越多
而redis和zookeeper实际上也是根据票数来决定,虽然redis里进行了特别说明,所以选举方式,都类似
但对于节点存在而言,基本都是根据半数机制,虽然可能并不完全的符合票数对应的节点
心跳信息默认每 2 秒传递一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
副本集中的副本节点在主节点挂掉后通过心跳机制检测到后,就会在集群内发起主节点的选举机制
自动选举出一位新的主服务器,从而保证系统可以正常运行
一个复制集里至少包含一个主节点
如下图中,三个节点,写操作只能在主节点操作
主节点收到数据操作时,会将操作记录在操作日志里
所有的从节点都会在数据同步的过程中,从主节点读取操作日志,从而保证从节点和主节点的数据统一
无仲裁节点 复制集搭建:
伪集群:在一台服务器下,创建N个mongo实例形成的集群
实际上是根据端口来区分,而不是ip,压力集中给一个节点
基本只在测试的时候进行操作,只要是可以指定端口启动的,一般都可以使用伪集群
如mongo,redis(虽然没有指定,但可以修改配置文件来修改端口)
实际上基本所有的服务器都可以操作伪集群,因为他们本质上也就是操作一个端口,除了固定的(可以自己百度找)
opt目录下创建replica_set目录(名称并不做要求):
[ root@A /]
mongo压缩包copy进去,然后解压:
[ root@A opt]
[ root@A opt]
[ root@A replica_set]
[ root@A replica_set]
进入mongo目录,创建下面3个配置文件,并修改其内容如下:
[ root@A mongodb-linux-x86_64-4.1.3]
dbpath = /data/mongo/data/server1
bind_ip = 0.0 .0.0
port = 37017
fork = true
logpath = /data/mongo/logs/server1.log
replSet = lagouCluster
从节点1配置:
[ root@A mongodb-linux-x86_64-4.1.3]
dbpath = /data/mongo/data/server2
bind_ip = 0.0 .0.0
port = 37018
fork = true
logpath = /data/mongo/logs/server2.log
replSet = lagouCluster
从节点2配置:
[ root@A mongodb-linux-x86_64-4.1.3]
dbpath = /data/mongo/data/server3
bind_ip = 0.0 .0.0
port = 37019
fork = true
logpath = /data/mongo/logs/server3.log
replSet = lagouCluster
保证配置中目录的存在:
mkdir -p /data/mongo/data/server1
mkdir -p /data/mongo/data/server2
mkdir -p /data/mongo/data/server3
mkdir -p /data/mongo/logs
初始化节点配置:
启动三个节点:
[ root@A mongodb-linux-x86_64-4.1.3]
[ root@A mongodb-linux-x86_64-4.1.3]
[ root@A mongodb-linux-x86_64-4.1.3]
[ root@A mongodb-linux-x86_64-4.1.3]
然后进入任意一个节点(我们进入37017),运行如下命令:
[ root@A mongodb-linux-x86_64-4.1.3]
启动后,我们进行show dbs进行查询,发现出错误,这是正常的(因为默认从不能进行读取,虽然是读写分离,但却不能读)
后面会进行解决,现在跳过即可
因为加了replSet配置,就代表了操作主从
所以你可以试着将该配置删除,发现就可以执行show dbs进行查询了
先做37017和37018两台机器的集群,一主一从:
var cfg = {
"_id" : "lagouCluster" ,
"protocolVersion" : 1 ,
"members" :[
{ "_id" :1,"host" : "192.168.164.128:37017" ,"priority" :10} ,
{ "_id" :2,"host" : "192.168.164.128:37018" }
]
}
rs.initiate( cfg)
rs.status( )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
_id:集群标识
protocolVersion:协议版本
priority:优先级,整数1~1000,越大,优先级越高,默认为1,设置为0则永不会选举成主
如果都没有设置优先级或者优先级一样,则默认为最先连接的节点为主节点(根据连接的时间戳 )
通常来说,谁来执行集群的设置,那么他就是主(第一次),但若执行太快,可能就与实际情况不同了(很少见)
但一般也自动修正的
若出现了且没有修正,可以删除对应的数据目录,然后重新操作,直到满足你的第一个为止
实际上并不需要必须规定谁是主,主要看你自己是否有强迫想法
而像redis和zookeeper基本不会出现相同的结果,所以不做考虑,假如出现了相同的
那么基本也是先启动(不是连接)容易变成主节点(相对于第一个来说)
变成节点后,对应的信息会保存好的,一般保存在数据库里面而不是日志里面(删除数据库也就没有了)
所以,就算关闭对应的mongo,下次启动时,对应的状态还是会变成原来的样子
节点的动态增删:
在主节点下进行操作(因为对节点的操作,一般只能由主节点来进行执行)
rs.add( "192.168.164.128:37019" )
rs.remove( "192.168.164.128:37019" )
复制集操作演示
进入主节点 ----- 插入数据 ------ 进入从节点验证
use diyi
db.yi.insert( { "name" : "sun" ,"age" :32} )
db.yi.find( )
use diyi
db.yi.find( )
注意:默认节点下从节点不能读取数据 (解决前面的问题),调用slaveOk解决
lagouCluster:SECONDARY> rs.slaveOk( )
vim /root/.mongorc.js
复制集群的选举机制:
为了保证高可用,在集群当中如果主节点挂掉后,会自动在从节点中选举一个重新做为主节点
我们需要在启动37019,否则单个机器是不会变成主的(没有通信,除非原来就是变成了主)
kill掉37017,37018会自动上位成主节点
当然,可能因为顺序的原因,会使得37019变成主,一般顺序在前,容易变成主,并不是全看优先级(已经操作了启动为主了)
当37017归来,37018会自动让位给37017,因为37017的优先级太高
在MongoDB 中通过在集群配置中的 属性值大小来决定选举谁做为主节点(如priority)
但选举的操作一般都是票数多的成为主,基本都是如此
有仲裁节点 复制集搭建 :
设置arbiterOnly 为 true 表示做为裁判节点用于执行选举操作,该配置下的节点永远不会被选举为主节点和从节点
举例:加入37020,并全部启动
var cfg = {
"_id" : "lagouCluster" ,
"protocolVersion" : 1 ,
"members" :[
{ "_id" :1,"host" : "192.168.164.128:37017" ,"priority" :10} ,
{ "_id" :2,"host" : "192.168.164.128:37018" ,"priority" :0} ,
{ "_id" :3,"host" : "192.168.164.128:37019" ,"priority" :5} ,
{ "_id" :4,"host" : "192.168.164.128:37020" ,"arbiterOnly" :true}
]
}
rs.reconfig( cfg)
rs.status( )
节点说明:
PRIMARY 节点:(主节点)可以查询和新增数据
SECONDARY 节点:(从节点)只能查询不能新增,基于priority 权重可以被选为主节点
ARBITER 节点:裁判,仲裁节点,不能查询数据和新增数据 ,不能变成主节点
OTHER节点:其他节点,没有在集群中的节点,或者说就是普通的节点,但是因为是离开了主从,所以出现的标识
实际上并不完全需要知道谁是主节点,通常都会寻找到(项目中)
所以说,他的选举方式,我们有时可以不必理会,了解即可(其他的中间件也差不多,比如redis,zookeeper等等)
和上面的配置步骤相同,另一种方式增加特殊的仲裁节点
注入节点执行 rs.addArb(“IP:端口”)
删除节点和之前一样
rs.addArb( "192.168.164.128:37020" )
rs.remove( "192.168.164.128:37020" )
分片集群 Sharded Cluster :
什么是分片
分片,是指将数据拆分,将其分散到不同的机器上
这样的好处就是,不需要功能强大的大型计算机也可以存储更多的数据,处理更大的负载
mongoDB 的分片,是将collection 的数据进行分割,然后将不同的部分分别存储到不同的机器上
当 collection 所占空间过大时,我们需要增加一台新的机器,分片会自动将 collection 的数据分发到新的机器上
为什么要分片:
存储容量需求超出单机磁盘容量
活跃的数据集超出单机内存容量,导致很多请求都要从磁盘读取数据
影响性能写IOPS(每秒处理IO量)超出单个MongoDB节点的写服务能力
随着数据的增长,单机实例的瓶颈是很明显的,虽然可以通过复制的机制应对压力(主从复制)
但MongoDB中单个集群的节点数量限制到了50个(之前是12个)以内,所以需要通过分片进一步横向扩展
此外分片也可节约磁盘的存储
分片技术,使得集合中的数据分散到多个分片集中,使得MongoDB具备横向的发展
分片的工作原理 :
分片集群由以下3个服务组成:
Shards Server:分片服务,每个shard由一个或多个mongod进程(复制集集群)组成,用于存储数据
Config Server:配置服务,用于存储集群的Metadata信息,包括每个Shard的信息和chunks信息(后面会讲到)
Route Server:路由服务,由Client连接,使整个Cluster(集群)看起来像单个DB服务器
要构建一个MongoDB Sharding Cluster(分片集群),需要三
种角色:
1、mongos (路由进程,应用程序接入 mongos 再查询到具体分片)数据库集群请求的入口:
所有的请求都通过 mongos 进行协调,不需要在应用程序添加一个路由选择器,mongos 自己就是一个请求分发中心,
它负责把对应的数据转发到对应的 shard 服务器上
在生产环境通常有多个 mongos 作为请求的入口,防止其中一个挂掉所有的 mongodb 请求都没有办法操作
2、config server(路由表服务,每一台都具有全部 chunk 的路由信息):
顾名思义为配置服务器,存储所有数据库元信息(路由、分片)的配置
mongos 本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据
mongos 第一次启动或者关掉重启就会从 config server 加载配置信息
以后如果配置服务器信息发生变化(比如增加一个分片节点,就会使得他变化)
就会通知到所有的mongos 更新自己的状态,这样 mongos 就能继续准确路由
在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,这个可不能丢失,所以一般都操作集群
即这时就算挂掉其中一台,只要还有存货,mongodb 集群就不会挂掉
3、shard(为数据存储分片。 每一片都可以是复制集replica set)
这就是传说中的分片了,假设一台机器的一个数据表 Collection1 存储了 1T 数据,那么压力太大了
再分给 3个机器后,每个机器都是 256G,则分摊了集中在一台机器的压力
事实上,上图3个分片如果没有副本集(replica set)是个不完整架构
假设其中的一个分片挂掉那三分之一的数据就丢失了
所以在高可用性的分片架构中,还需要对每一个分片构建 replica set 副本集保证分片的可靠性(高可用)
所以总体来说,上面的分片包含多个服务器,实际上在项目里会更多
生产环境通常是 2 个副本 + 1 个仲裁,以及分片节点入口,通常主代表分片节点入口
shard和chunk :
片键(shard key) :
为了在集合中分配文档(也就是数据),MongoDB使用分片主键分割集合(根据哪个键来将数据拆分到其他分片集群中)
分片主键由不重复的字段或者字段集合组成
对一个集合分片时,你要选择分片主键,分片主键在分片以后不能修改,一个分片集合只有一个分片主键
为了对非空的集合进行分片,集合必须有一个以分片主键开头的索引
对于空集合,如果集合对于分片主键没有一个合适的索引,MongoDB将创建索引
分片主键的选择将影响分片集群的性能、效果和扩展能力
一个最佳的硬件和基础设施的集群的瓶颈取决于分片主键的选择
分片主键的选择将影响你的集群使用的分片策略
区块(chunks) :
MongoDB分割分片数据到区块(分割后的数据的里面的数据,存的是块,简称是更小的存储单位)
每一个区块包含基于分片主键的左闭右开的区间范围
在分片集群中,MongoDB通过分片迁移区块,使用分片集群权衡器
权衡器视图完成一个公平的区块平衡,通过集群中所有的分片
分片策略:
范围分片(Range based sharding):
范围分片是基于分片主键的值切分数据,每一个区块将会分配到一个范围
范围分片适合满足在一定范围内的查找,例如查找X的值在【100-200】之间的数据
mongo 路由根据Config server中存储的元数据,可以直接定位到指定的shard的Chunk中
缺点:如果shardkey有明显递增(或者递减)趋势,则新插入的文档多会分布到同一个chunk,无法扩展写的能力
哈希分片(Hash based sharding):
Hash分片是计算一个分片主键的hash值,每一个区块将分配一个范围的hash值
Hash分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围分片的不足
缺点:不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找出满足条件的文档
合理的选择shard key:
选择shard key时,要根据业务的需求及『范围分片』和『Hash分片』2种方式的优缺点合理选择
要根据字段的实际原因对数据进行分片,否则会产生过大的Chunk(热块)
分片搭建架构图 (下图中进行了修改,红色部分原来是2,修改成1):
分片集群的搭建过程:
配置 config 节点集群:
将mongo解压到根目录,改名为shard_cluster
进入shard_cluster并创建config目录用来存集群节点的配置文件
[ root@A opt]
[ root@A opt]
[ root@A /]
[ root@A /]
[ root@A shard_cluster]
[ root@A shard_cluster]
第一个配置节点:
[ root@A config]
dbpath = /data/mongo/config1
bind_ip = 0.0 .0.0
port = 17017
fork = true
logpath = /data/mongo/logs/config1.log
replSet = configCluster
configsvr = true
第二个配置节点:
[ root@A config]
dbpath = /data/mongo/config2
bind_ip = 0.0 .0.0
port = 17018
fork = true
logpath = /data/mongo/logs/config2.log
replSet = configCluster
configsvr = true
配置文件中的目录需要手动创建出来(不存在的话):
[ root@A config]
[ root@A config]
[ root@A config]
启动(也可以叫做连接客户端)两台mongo,并进入任意节点shell 并添加配置节点集群,注意,要进入admin库操作
./bin/mongo --port 17017
use admin
var cfg = {
"_id" : "configCluster" ,
"protocolVersion" : 1 ,
"members" :[
{ "_id" :1,"host" : "192.168.164.128:17017" } ,
{ "_id" :2,"host" : "192.168.164.128:17018" }
]
}
rs.initiate( cfg)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
配置 shard 节点集群:
分片1:shard1集群搭建37017到37019
创建data对应的数据目录:
mkdir -p /data/mongo/shard1-37017
mkdir -p /data/mongo/shard1-37018
mkdir -p /data/mongo/shard1-37019
mkdir -p /data/mongo/logs
创建配置文件:
vim shard1-37017.conf
vim shard1-37018.conf
vim shard1-37019.conf
dbpath = /data/mongo/shard1-37017
bind_ip = 0.0 .0.0
port = 37017
fork = true
logpath = /data/mongo/logs/shard1-37017.log
replSet = shard1
shardsvr = true
dbpath = /data/mongo/shard1-37018
bind_ip = 0.0 .0.0
port = 37018
fork = true
logpath = /data/mongo/logs/shard1-37018.log
replSet = shard1
shardsvr = true
dbpath = /data/mongo/shard1-37019
bind_ip = 0.0 .0.0
port = 37019
fork = true
logpath = /data/mongo/logs/shard1-37019.log
replSet = shard1
shardsvr = true
./bin/mongo --port 37017
var cfg = {
"_id" : "shard1" ,
"protocolVersion" : 1 ,
"members" :[
{ "_id" :1,"host" : "192.168.164.128:37017" } ,
{ "_id" :2,"host" : "192.168.164.128:37018" } ,
{ "_id" :3,"host" : "192.168.164.128:37019" }
]
}
rs.initiate( cfg)
rs.status( )
分片2:shard2集群搭建47017到47019:
创建data数据目录:
mkdir -p /data/mongo/shard2-47017
mkdir -p /data/mongo/shard2-47018
mkdir -p /data/mongo/shard2-47019
mkdir -p /data/mongo/logs
创建配置文件:
vim shard2-47017.conf
vim shard2-47018.conf
vim shard2-47019.conf
dbpath = /data/mongo/shard2-47017
bind_ip = 0.0 .0.0
port = 47017
fork = true
logpath = /data/mongo/logs/shard2-47017.log
replSet = shard2
shardsvr = true
dbpath = /data/mongo/shard2-47018
bind_ip = 0.0 .0.0
port = 47018
fork = true
logpath = /data/mongo/logs/shard2-47018.log
replSet = shard2
shardsvr = true
dbpath = /data/mongo/shard2-47019
bind_ip = 0.0 .0.0
port = 47019
fork = true
logpath = /data/mongo/logs/shard2-47019.log
replSet = shard2
shardsvr = true
./bin/mongo --port 47017
var cfg = {
"_id" : "shard2" ,
"protocolVersion" : 1 ,
"members" :[
{ "_id" :1,"host" : "192.168.164.128:47017" } ,
{ "_id" :2,"host" : "192.168.164.128:47018" } ,
{ "_id" :3,"host" : "192.168.164.128:47019" }
]
}
rs.initiate( cfg)
rs.status( )
配置 mongos 路由节点(类似于负载均衡的角色):
创建配置文件:
vim route-27017.conf
port = 27017
bind_ip = 0.0 .0.0
fork = true
logpath = /data/mongo/logs/route.log
configdb = configCluster/192.168.164.128:17017,192.168.164.128:17018
mongos中添加分片节点:
启动路由使用mongos命令,而不是mongo或mongod(他们两个有多种启动方式)
但我们通常使用配置文件的启动,这里使用他们的启动方式会报错:
[ root@A shard_cluster]
进入路由和普通的节点一样,使用mongo:
[ root@A shard_cluster]
sh.status( )
use admin
sh.addShard( "shard1/192.168.164.128:37017,192.168.164.128:37018,192.168.164.128:37019" )
sh.addShard( "shard2/192.168.164.128:47017,192.168.164.128:47018,192.168.164.128:47019" )
指定片键 并 设置分片大小 :
使用mongos完成分片开启和分片大小设置
sh.enableSharding( "lagou" )
sh.shardCollection( "lagou.emps" ,{ "name" : "hashed" } )
由于哈希分片并不是完全的平均,即数据很可能明显分配得不均匀,这时就需要用到迁移区块了,而由于默认chunk的大小是64M
若我们插入的数据量不大,估计也不会产生几个chunks,从而很难出现区块的差值,使得不会进行迁移区块
因为chunk迁移需要满足一定的条件:
虽然使用的是哈希分片,但并不会是完全的平均,这时就需要用到迁移了
当分片集群中,各个分片的副本集中,块的数量有差值,根据上面的相差值来进行迁移
所以,为了能够测试看的很清楚,我们调整下chunk的大小为1M:
use config
db.settings.save( { _id:"chunksize" ,value:1} )
db.settings.find( )
插入数据测试 :
通过路由(在路由节点下操作)循环向集合中添加数据
for( var i = 1 ; i <= 1000 ; i++) {
db.emps.insert( { "_id" :i,"name" : "test" +i} )
}
测试:
在shard1和shard2分片中,分别执行如下,观察结果
发现的确进行了分区块操作,将数据根据策略分发给各个副本集,也就是分片节点
安全认证:
安全认证概述
MongoDB 默认是没有账号的,可以直接连接,无须身份验证
实际项目中肯定是要权限验证的,否则后果不堪设想
从2016年开始发生了多起MongoDB黑客赎金事件,大部分MongoDB安全问题暴露出的短板其实是用户
首先用户对于数据库的安全不重视,其次用户在使用过程中可能没有养成定期备份的好习惯
最后是企业可能缺乏有经验和技术的专业人员,所以对MongoDB进行安全认证是必须要做的
用户相关操作 :
切换到admin数据库对用户的添加
创建 MongoDB 登录用户以及分配权限的方法:
db.createUser( {
user : "账号" ,
pw : "密码" ,
roles : [
{ role: "父亲角色" , db: "养育孩子" } ,
{ role: "职员角色" , db: "为企业工作" }
]
} )
user:创建的用户名称,如 admin、root 、lagou
pwd:用户登录的密码
roles:为用户分配的角色,不同的角色拥有不同的权限,参数是数组,可以同时设置多个
role:角色,MonngoDB 已经约定好的角色,不同的角色对应不同的权限 后面会对role做详细解释
db:数据库实例名称,如 MongoDB 4.0.2 默认自带的有
admin、local、config、test 等,即为哪个数据库实例 设置用户
举例:
无认证的启动mongo,执行创建用户等操作(随便一般配置文件的启动,设置端口为27020,参照上一章中的那个配置)
如:
dbpath = /data/mongo/
port = 27020
bind_ip = 0.0 .0.0
fork = true
logpath = /data/mongo/MongoDB.log
logappend = true
auth = false
use admin
db.createUser( {
user : "laosun" ,
pw : "123123" ,
roles : [
{ role:"root" , db:"admin" }
]
} )
修改密码:
db.changeUserPassword( 'laosun' ,'123321' )
用户添加角色 :
db.grantRolesToUser( '用户名' , [ { role: '角色名' , db: '数据库名' } ] )
关闭mongo,以安全认证的方式启动mongo
以auth 方向启动mongod:
./bin/mongod -f mongo.conf --auth
也可以在mongo.conf 中添加auth=true 参数
输入如下(验证用户):
db.auth("账号","密码") #db.auth("laosun","123123")
#验证好后,那么我们就是这个角色了,这时就可以操作了(对所有执行都可以操作,因为是root)
删除用户:
db.dropUser( "用户名" )
角色:
数据库内置的权限 :
read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
userAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
一般来说,他们的指定的数据库就是他们操作的对象
clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限
readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限
root:只在admin数据库中可用,超级账号,超级权限(基本所有都可以操作)
一般来说,由于他们指定的是admin,那么他们一般操作的数据库是全部
角色对应的权限 :
数据库用户角色:read、readWrite
数据库管理角色:dbAdmin、dbOwner、userAdmin
集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager
备份恢复角色:backup、restore
所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
超级用户角色:root
这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)
总结:
生产环境用read和readWrite
开发人员用root,运维人员backup和restore
安全认证实现流程:
创建管理员:
MongoDB 服务端开启安全检查之前,至少需要有一个管理员账号
一般admin 数据库中的用户都会存放管理员(管理员角色),或者普通角色
如果 admin 库没有任何用户的话
一般我们也会在其他数据库里进行存放普通用户,但不可存放管理员(因为对应的管理员角色基本只能操作admin)
因为这是防止被用户操作我们的表,所以要有明确的分工
若都没有且启用身份验证来启动mongo
那么连接后,有些权限是可以访问的,比如show dbs(虽然不会报错且基本没有显示)
在有用户的情况下,不能访问,必须先验证
即仍然可以不验证账号密码照样能进行一些的权限操作,安全认证相当于对他无效
也有可能甚至会出现CRUD都可以操作,虽然一般都不会
CRUD:
CRUD说的就是增查改删
C:Create 增加对应CREATE(create)
R:Retrieve(找回)查询SELECT(select)
U:Update修改UPDATE(update)
D:Delete删除 DELETE(delete)
use admin
db.createUser( {
user:"root" ,
pwd:"123123" ,
roles:[ { role:"root" ,db:"admin" } ]
} )
创建普通用户 :
如下所示 mydb是自己新建的数据库(可以自己创建一个数,后面有用的)
没安全认证之前(不使用安全认证的启动)可以随意 CRUD
其余的都是 mongoDB(我的版本是4.1.3)自带的数据库(对应配置文件只是单纯的配置文件,所以并没有消除库)
为 admin 库创建管理员之后,现在来为普通数据库创建普通用户,以 mydb 为例
方式与创建管理员一致,切换到指定数据库进行创建即可
如下所示,为 mydb 数据库创建了两个用户,zhangSan 拥有读写权限,liSi 拥有只读权限,密码都是 123123
注意:大小写敏感(即S与s不一样)
use mydb
db.createUser( {
user:"zhangSan" ,
pwd:"123123" ,
roles:[ { role:"readWrite" ,db:"mydb" } ]
} )
db.createUser( {
user:"liSi" ,
pwd:"123123" ,
roles:[ { role:"read" ,db:"mydb" } ]
} )
在客户端关闭 MongoDB 服务
use admin
db.shutdownServer( )
MongoDB 安全认证方式启动 :
修改对应的配置文件:
dbpath = /data/mongo/
port = 27020
bind_ip = 0.0 .0.0
fork = true
logpath = /data/mongo/MongoDB.log
logappend = true
auth = true
直接启动即可,不用指定–auth了
分别以普通用户和管理员登录验证权限:
普通用户现在仍然像以前一样进行登录,如下所示直接登录进入 mydb 数据库中,登录是成功的
只是登录后日志少了很多东西(因为是使用了auth为true的登录,而auth为false则要多很多,默认的)
而且执行 show dbs 命令,以及 show tables 等命令都是失败的
即使没有被安全认证的数据库,用户同样操作不了,这都是因为权限不足
一句话:用户只能在自己权限范围内的数据库中进行操作
执行show dbs出现错误:
如下所示,登录之后必须使用 db.auth(“账号”,“密码”) 方法进行安全认证,认证通过,才能进行权限范围内的操作
show dbs 已经看不到其它未拥有操作权限的数据库了,显然它也操作不了其它未授权的数据库
因为创建的用户 zhangSan 拥有对 mydb 的读写权限,所有我们进行登录验证,使得我们得到权限
db.auth( "shangSan" ,"123123" )
上面在mydb里面的集合a里创建了一个数据,并读取了
注意:当你进行多次验证时会出现问题:
如上错误码所示,是因为mongo的shell命令的bug
因为切换了太多不同数据库的用户(同一个数据库的用户验证,那么就是改变用户,而不会报错),是一个坑
这是使得我们基本只能操作一个数据库,而防止过与频繁的切换,因为登录认证的基本是根据数据库来的
实际上只要切换一次就会出现错误,为了解决这个错误,可以exit退出mongo,然后重新进入
然后执行use mydb和db.auth认证
这时就又可以进行读取操作了,即又回来了,这时我们切换当前数据库的liSi用户,发现可以读取,但不能写入,并没有出现错误
发现,当前数据库的用户可以切换,但要注意不能切换不同数据库的
注意 db.auth 认证必须在对应的数据库下进行
比如为 db1 数据库单独创建的用户 dbUser不能在 db2 数据库下执行 db.auth 验证操作,否则是失败的
只能到 db1 下去验证(绑定的)
除了登录后再使用 db.auth(“账号”,“密码”) 方法进行验证之外,也可以像 Mysql 一样,在连接的同时指定 账号与密码
mongo localhost:27017/mydb1 -u "账号" -p "密码"
[ root@A mongodb-linux-x86_64-4.1.3]
客户端管理员登录如下所示 管理员 root 登录,安全认证通过后,拥有对所有数据库的所有权限
use admin
db.auth( "root" ,"123123" )
上面是操作一个机器的,现在我们操作多个机器
分片集群认证 :
认证之前 进入路由 :
[ root@A shard_cluster]
创建管理员:
use admin
db.createUser( {
user:"root" ,
pwd:"123123" ,
roles:[ { role:"root" ,db:"admin" } ]
} )
普通用户:
use laosundb
db.createUser( {
user:"laosun" ,
pwd:"123123" ,
roles:[ { role:"readWrite" ,db:"laosundb" } ]
} )
关闭所有的节点:
如配置节点、分片节点、路由节点
一个一个关闭太累了,搞一个小工具,可以快速关闭进程
[ root@A /]
[ root@A /]
[ root@A /]
[ root@A /]
其中,我们可以发现,有两个不同的文件,./bin/mongod和./bin/mongos,后缀是mongod和mongos,对应的文件是启动的开始
而killall后面指定的就是文件的最后一个后缀(或者是完整名)
所以说上面的组合基本上可以将全部的mongo进程杀死,当然你可以进行其他测试
但也要注意:有些是需要强制的杀死的,否则一般会再次出现(一般是父进程的原因)
如使用killall -9 后面接后缀,进行强制杀死
生成密钥文件 并修改权限:
密钥文件是各个节点直接通信的凭证(通关文牒)
另一种通关文牒:509数字证书(百度自行查阅)
赋予权限只能是600
该文件的权限大概率会被检查,若不是600
可能根据该配置文件启动时,会启动不了(我测试过,的确启动不了,报错了)
也有可能某些操作中,会报错,甚至会执行不了,因为这里是密码所在的地方
[ root@A /]
[ root@A /]
[ root@A /]
OpenSSL简介:
在所有的类 Unix 发行版、Solaris、Mac OS X 和 Windows 中
默认都用openssl这个工具来生成高强度随机密码(这个是系统自带,使用率最高)
[ root@A ~]
注:上面的命令将生成一个随机的、长度为 6 个字符的高强度密码,这种方式不支持同时生成多个密码
我们强烈推荐你生成 14 个字符的密码,当然你可以使用 OpenSSL 生成任意长度的密码
不是说6位长度吗,怎么显示的不是6位,因为你生成的位数不足以达到高强度密码要求
所以默认情况下openssl会给你添加字符,使其保证密码高强度(这也使得无论你多少位,基本都会有添加)
添加规则:
配置节点和分片节点 设置密钥文件(太多了自己慢慢加吧(●ˇ∀ˇ●)) :
向 对应的xxx.conf 文件中追加如下内容:
auth = true
keyFile = /data/mongodb/testKeyFile.file
路由节点 设置密钥文件 (使得对应的相同密码的可以被路由操作,不同的基本不能加入集群,即不会被操作,大概是):
切记:路由不需要认证
keyFile = /data/mongodb/testKeyFile.file
启动所有节点:
挨个启动太累,可以编写一个shell 脚本批量启动
[ root@A /]
[ root@A shard_cluster]
[ root@A shard_cluster]
[ root@A shard_cluster]
[ root@A shard_cluster]
下面内容拷贝到startup.sh文件中:
./bin/mongod -f config/config-17017.conf
./bin/mongod -f config/config-17018.conf
./bin/mongod -f config/shard1-37017.conf
./bin/mongod -f config/shard1-37018.conf
./bin/mongod -f config/shard1-37019.conf
./bin/mongod -f config/shard2-47017.conf
./bin/mongod -f config/shard2-47018.conf
./bin/mongod -f config/shard2-47019.conf
./bin/mongos -f config/route-27017.conf
认证测试:
进入路由节点(测试管理账号)
mongos> use admin
mongos> show dbs
mongos> db.auth( "root" ,"123123" )
mongos> show dbs
测试普通用户(避免认证用户过多的错误,退出路由重新进)
mongos> use laosundb
mongos> show dbs
mongos> db.auth( "laosun" ,"123123" )
mongos> show dbs
注意:对应的分片是操作数据的,而不是配置,即在路由的认证,不会给到其操作的节点,只有数据会给到
Spring boot 连接安全认证的分片集群
参照83章博客的对应配置连接,并学习了88章博客的内容,那么这里你就知道是怎么回事了:
spring.data.mongodb.username= 账号
spring.data.mongodb.password= 密码
MongoDB监控:
MongoDB Ops Manager 简介
MongoDB Ops Manager(MMS) 是用于监控和备份MongoDB的基础设施服务
MongoDB OPS Manager是一个Web应用程序,它需要1个mongodb数据库
这个数据库是用来支持本身的MongoDB OPS Manager来运行的
因此,如果我们想要MongoDB OPS Manager运行起来,最少也需要安装一个MongoDB数据库
Ops Manager 作用:
简易的自动化数据库部署、扩展、升级和任务管理
通过 OPS 平台提供的超过 1 00 项仪表、图表,可以对 mongodb 进行多种监控
支持单节点、分片集群的备份和恢复
安装 Ops Manager:
下载安装包:
安装包的大小根据版本的不同略有差异,但维持在1 GB左右
官网: https://www.mongodb.com/subscription/downloads/archived
往下滚动,各种版本都有, mongo4对应mms4, mongo5对应mms5
版本要对应,防止出现问题
最好完全相同,而不是所有的4版本或者5版本,如操作4.1.3或者操作4.1.4,当然选择4.1.3,最好不选择4.1.4
这里就下载mms4,因为我们的mongo是mongodb-linux-x86_64-4.1.3,即版本是4.1.3
点击右下角的tar.gz,即可下载
上传到服务器并解压:
root@A opt]
解压完成后改个名字,名字太长了:
root@A opt]
编辑配置文件(conf-mms.properties):
[ root@A opt]
[ root@A conf]
根据自己的mongodb 进行配置,修改该文件的对应的地址端口为27020(因为我们正好有个单机的27020进行测试)
操作时记得需要先启动一个27020的mongdb实例
mongo.mongoUri= mongodb://127.0.0.1:27020/?maxPoolSize= 150
mongo.ssl= false
mongo.mongoUri= mongodb://127.0.0.1:27017/?maxPoolSize= 150
mongo.ssl= false
当前目录的mms.conf:
[ root@A conf]
BASE_PORT = 8080
BASE_SSL_PORT = 8443
JAVA_MMS_UI_OPTS = "${JAVA_MMS_UI_OPTS} -Xss328k -Xmx2048m -Xms2048m -XX:NewSize=600m
-Xmn1500m -XX:ReservedCodeCacheSize=128m -XX:-OmitStackTraceInFastThrow"
一般修改端口 和 内存 如果虚拟机内存不太够 可以适当减少内存配置
比如 -Xmx4352m -Xms4352m 改成 -Xmx2048m -Xms2048m,即4352mb修改成2048mb
上面就是修改的,修改为2g左右,即2048,而不是4g左右,即4352,当然并不是一定是1024的倍数
启动 mms:
启动之前确保 Ops Manager对应的 MongoDB 数据库已经启动
[ root@A mongodb-mms]
启动时间漫长,多等一会,如果出现以下的信息:
Check /opt/mongodb-mms/logs/mms0.log and /opt/mongodb-mms/logs/mms0-startup.log for errors
那么一般是启动失败,请查看 /opt/mongodb-mms/logs/mms0-startup.log 日志文件查看错误信息
对应的错误一般是由于空间不足引起的(会判断,当前的最高内存是否足够他的最高内存,一般并不会直接占用固定的内存)
cat logs/mms0-startup.log
我们可以通过修改虚拟机的内存为3G或者更多
当然在运行或者挂机情况下,基本只能提高内存,而不能减少内存,主要是防止文件的存储被移除,其中运行的情况下
即可以直接进行跳转的增加,也可以点击上键,但挂机情况下,只能4mb的增加,只能点击上那个键
或者修改mms.conf中将 内存大小设置为2G或者更少
当然他们可以都进行设置,基本上只要是虚拟机的内存大于mms.conf文件设置的内存就可以
可能小点或者等于也可以,但最好大于
重启虚拟机,一切重新来过再试
访问 mms 首页:
这里的端口是在 mms.conf 中配置的 : http://主机:端口,我这里就是http://192.168.164.128:8080
前提是,8080端口没有被其他程序占用,否则虽然启动成功(日志并没有报错,但实际上是报错的,只是没有给我们看到而已)
即是访问不了他的(只能访问对应的端口的存在的访问,虽然一般是404,如zookeeper的启动)
因为没有入口了,那么我们可以修改对应的端口,如8080修改成8089等等进行测试
或者让占用端口的关闭,如zookeeper就会占用8080端口
访问后,若出现如下,则代表操作成功
配置 Ops Manager
注册账号:
该账号密码,是我的浏览器的缓存,不需要理会
这里一定要注意:假设在下面的配置中,没有黄星星的,因为该缓存加上了,最好删除
否则可能下一步不了(因为大多数通过不了验证)
登录不了的,可能之所以出现,大概是有对应的ip的作用吧
保存了192.168.164.128的一些账号密码,进行了使用,一般是最近的优先
真正的账号密码需要注册
往下滑可以看到注册的文字,点击即可
帐号虽然说要邮箱,但可以不用邮箱 ,这时使用"laosun"
密码的要求较高, 这里使用"Abc_1 231 23"
下面进行了翻译:
点击创建账户即可,然后就会到如下的界面:
OPS Manager的配置页面 :
注意:下面的url地址填写自己的(在这里使用了另外一个地址,而不是192.168.164.128的地址)
配置的过程很多,选项也很多,只填写黄色星号的即可(上面只给出了黄色的星号的填写)
95%的选项选择默认值即可(即不需要填写)
上面基本是随便写的,因为对应的mms在本地,所以对应的配置也就随便设置了
点击下一步(后面也需要点击),直到出现如下:
代表对应的服务器里面没有该目录,即会通过不了验证(下一步不了)
需要自己创建一个mms的版本目录
[ root@A mongodb-mms]
至此添加后,再次点击下一步,发现,注册成功,并自动的给你登录了,如出现如下,则代表操作成功:
配置 MongoDB Ops ManagerAgent
点击下面界面中的 Manage your existing deployment(在登录后的一开始的界面里可以找到,翻译:管理你现有的部署)
然后出现如下:
翻译如下:
单击绿色 install Agent:
根据我们目标数据库的操作系统,我们可以选择相应的Agent来安装
实际的操作系统版本可能更多,大致上一致即可,只要能装上,如点击如下
根据弹出的安装步骤 安装 agent(代理):
里面都是对应的例子,具体步骤需要自己进行操作
下面进行了翻译,防止看不懂的
接下来我们进行操作,回到服务器(Xshell),输入如下(一步一步来,对应的使用自己的,下面只是进行参照):
curl -OL http://192.168.164.128:8080/download/agent/automation/mongodb-mms-automation-agent-manager-6.3.1.5645-1.x86_64.rhel7.rpm
sudo rpm -U mongodb-mms-automation-agent-manager-6.3.1.5645-1.x86_64.rhel7.rpm
接下来我们点击生成密钥,会发现下面的这里会出现数据:
生成后,以后再次进入,就基本没有该按钮了,需要点击代理API密钥进行查看:
假设你忘记了,那么可以进行删除,那么生成密钥的按钮就会重新出现,只是可能会有如下提示,当然现在可以无视
接下来继续跟着下一步:
sudo vi /etc/mongodb-mms/automation-agent.config
mmsGroupId = 62ec7e73c8042ae42592d959
mmsApiKey = 62ec8dd5c8042ae42592e2c211463a7380314c1b6120f059b4b17474
mmsBaseUrl = http://192.168.164.128:8080
sudo systemctl start mongodb-mms-automation-agent.service
sudo /sbin/service mongodb-mms-automation-agent start
启动后,点击验证代理,若出现如下,则代表验证成功:
点击继续,他会继续安装对应的代理,我们等待全自动安装完成即可,如下图所示:
至此操作成功
监控现有的Sharding Cluster服务:
确保 shard 集群已启动:
确保路由 、配置集群、 分片集群 都已经启动
每个节点的配置文件中
注意dbpath 和 logpath的绝对路径要存在,不要启动后中途删除,否则监控一般有警告,当然不存在的话也是启动不了的
注释下面安全认证的配置(无论是路由,配置,分片等等,只要是有下面的两个中的一个或者两个,都注释,然后启动)
即我们先不操作认证的安全
启动所有节点,我们执行前面的脚本即可
在安装好的agent界面点击继续:
点击后,一般就会到如下的界面:
配置监控服务 单机实例或者集群实例都可以:
其中我们输入自己的ip地址和端口,我们这里就是192.168.164.128,和27017(这个是路由的端口),如下:
其他的选项现在不需要操作,点击继续,他会先看看对应的该端口的mongo是否启动(存在),若启动(存在)
等待一会,会出现如下:
这时我们可以再次点击继续即可,那么会出现(相关的集群都会出现):
至此操作完成
我们再次点击继续会找到的,那么会出现如下:
将我们了解那里点击勾勾,继续点击继续即可
一般集群发现要求配置节点和分片节点必须 是绝对路径 ,否则到这个界面一般会出现警告,注意一下即可
点击继续后,我们直接点击返回,然后再次点击上面的(不,只是监控即可),实际上并不需要点击继续,只是你可以看看结果(也就是上面的集群显示)
然后什么都不需要管了
然后可以在这里查看到如下:
注意:上面的页面这个时候最好不要操作显示中文,可能会出错(一般需要刷新测试,因为检查完了,那么显示中文就不会出错)
大概是有检查页面数据的原因,则中文使得导致不匹配了,从而显示错误,如:
点击METRICS,然后到如下:
至此这些进行我们可以看到的信息
点击shard1,查看具体的分片 和 监控指标:
至此我们就可以观察对应的分片数据了
最后注意一下:可能对应的mms会突然关闭,再次启动即可,一般关闭是因为对应的内存或者他指定的数据库的操作造成的
具体的可以进行百度
数据备份与恢复:
备份的目的:
防止硬件故障引起的数据丢失防止人为错误误删数据
时间回溯(时光机)
监管要求
数据备份:
在MongoDB 中我们使用 mongodump 命令来备份MongoDB数据
该命令可以导出所有数据到指定目录中
mongodump 命令可以通过参数指定导出的数据库或者集合
mongodump 命令脚本语法如下:
mongodump -h dbhost -d dbname -o dbdirectory
-h(对应的host的简写,后面可以加上–port,或者前面加上):
MongoDB所在服务器地址,例如:127.0.0.1,当然也可以指定端口号:127.0.0.1:37017
–db 或者 -d :需要备份的数据库实例, 例如:laosundb
-o:备份的数据存放位置
例如:/root/bdatas 在备份完成后,自动建立/root/bdatas目录,这个目录里面存放备份数据
–collection可以简写成–c
举例:
[ root@a mongodb]
[ root@a mongodb]
./bin/mongodump --host= 192.168 .164.128 --port= 27020 -c emps -d test -o= /data/emps_bak
导出的数据目录中:一个数据库对应一个目录
即根据上面的例子,知道/data/bdatas目录下面有个test目录,目录里面 一个集合对应2个文件(bson和metadata.json)
bson文件中记录数据
metadata.json文件中记录集合的结构等元数据
一般该对应的文件会加上前缀,假如test有两个集合,如ad和emps,那么使用第一个方式操作时,会出现四个文件,分别是:
ad.bson,ad.metadata.json和emps.bson,emps.metadata,而使用后面的那个方式,只会出现emps.bson,emps.metadata
这是有区别的
数据恢复:
mongodb使用 mongorestore 命令来恢复备份的数据
mongorestore 命令脚本语法如下:
mongorestore -h < hostname> < :port> -d dbname < path>
–host <:port> 或 -h <:port>:MongoDB所在服务器地址默认为: localhost:37017
–db 或 -d :需要恢复的数据库实例
例如:test,当然这个名称也可以和备份时候的不一样,比如test2(因为没有的话,会创建该数据库)
–drop:恢复的时候,先删除当前数据,然后恢复备份的数据
就是说,恢复后,备份后添加修改的数据都会被删除,慎用哦
举个例子:原来备份了3条数据,中途我们创建了2条数据,总共5条数据
这时我们使用这个,那么就回到了3条数据,即备份后,我们创建的2条数据被删除了
< path/>:mongorestore 最后的一个参数,设置备份数据所在位置,例如:/root/bdatas/laosundb
你不能同时指定< path/>和 --dir 选项,–dir也可以设置备份目录
注意:恢复指定的数据库 需要在恢复的路径中出现数据库的名字
–dir:指定备份的目录你不能同时指定< path/>和 --dir 选项
举例:
[ root@a mongodb]
[ root@a mongodb]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
如果指定-d参数,则数据目录必须指定到库目录
如果没指定库的目录,那么-d参数也不可以指定
总之:-d和库目录test要么同时存在,要么同时删除
全量加增量备份和恢复案例:
注意的问题:
这里我们使用前面操作的复制集的mongo(可以看看看本章最前面的那个)
删除复制集中原来的数据文件目录/data/mongo/data/server1
重新建立数据目录data
重新启动复制集中的实例,进入37017重新进行复制集的配置
var cfg = {
"_id" : "lagouCluster" ,
"protocolVersion" : 1 ,
"members" : [
{ "_id" :1,"host" : "192.168.164.128:37017" ,"priority" :10} ,
{ "_id" :2,"host" : "192.168.164.128:37018" } ,
{ "_id" :3,"host" : "192.168.164.128:37019" }
]
}
rs.initiate( cfg)
rs.status( )
进入mongodb 插入两条数据:
use laosundb
db.emps.insert( { name:"aaa" ,salary:1} )
db.emps.insert( { name:"bbb" ,salary:2} )
进行全量备份:
备份之前:将fullbackup和oplog_bak目录删掉(没有的话,请忽略删除目录的步骤)
[ root@a mongodb]
[ root@a mongodb]
继续插入数据 并更新 :
use laosundb
db.emps.insert( { name:"ccc" ,salary:3} )
db.emps.insert( { name:"ddd" ,salary:4} )
db.emps.insert( { name:"eee" ,salary:5} )
db.emps.update( { name:"ccc" } ,{ $set :{ salary:333} } )
db.emps.update( { name:"ddd" } ,{ $set :{ salary:444} } )
做增量备份:
root@a mongodb]
删除所有的数据:
use laosundb
db.emps.remove( { } )
db.emps.find( )
先恢复全量数据:
[ root@a mongodb]
use laosundb
db.emps.find( )
恢复数据到指定的时间点
修改数据备份文件:
oplog.rs.bson -> oplog.bson (去掉中间的rs),并且删除meta元数据文件
[ root@a ~]
[ root@a local]
[ root@a local]
一般需要上面的这样操作,否则可能不会恢复(报错)
找出第一次更新的时间:
对应的数据里面包括了op的key和u的value,以及ts的key和对应的value值,我们根据对应的ts升序来进行查看
op:operation(中文意思:活动)
u:update(中文意思:更新)
ts:timestamp(中文意思:时间戳)
1:升序(时间最近的靠前)
use local
db.oplog.rs.find( { "op" : "u" } ) .sort( { "ts" :1} )
恢复到指定的时间点的数据:
[ root@a mongodb]
root@a mongodb]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
我的例子:
出现的三个中,只要使用妻子一个,一般就会根据顺序出现相应的记录
具体可以自己进行测试,可能这有些问题,虽然我测试的结果是这样,但你还是继续测试一下吧
查看数据恢复:
use laosundb
db.emps.find( )
定时备份:
创建备份数据的存放目录
mongo_bak_now:数据备份的临时目录
mongo_bak_list:备份数据压缩文件的存放目录
[ root@a /]
编写备份脚本:
[ root@a /]
对应的mongobk.sh数据:
#!/bin/sh
DUMP = /opt/replica_set/mongodb-linux-x86_64-4.1.3/bin/mongodump
OUT_DIR = /mongo/backup/mongo_bak/mongo_bak_now
TAR_DIR = /mongo/backup/mongo_bak/mongo_bak_list
DATE = ` date +%Y_%m_%d_%H_%M_%S`
DAYS = 7
TAR_BAK = "mongod_bak_$DATE .tar.gz"
cd $OUT_DIR
rm -rf $OUT_DIR /*
mkdir -p $OUT_DIR /$DATE
$DUMP -h 127.0 .0.1 --port 37017 -o $OUT_DIR /$DATE
tar -zPcvf $TAR_DIR /$TAR_BAK $OUT_DIR /$DATE
find $TAR_DIR / -mtime +$DAYS -delete
exit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
修改脚本权限:
[ root@a mongodb]
编辑crontab:
[ root@a mongodb]
30 2 * * * /mongo/backup/mongobk.sh
测试的时候 可以改成 一分钟备份一次:
* * * * * /mongo/backup/mongobk.sh
查看crontab 的状态:
[ root@a mongodb]
这时,到对应的目录下,看看是否有文件出现,若有,则代表操作成功
如果没有启动,可以使用下面的命令启动定时服务和加入开机自启动(因为需要完全的自动操作,也是需要开机自启的):
[ root@a mongodb]
[ root@a mongodb]
[ root@a mongodb]
查看定时任务和删除定时任务:
[ root@a mongodb]
[ root@a mongodb]
[ root@a mongodb]
至此,只要有这个备份,那么一般来说,基本就可以随意的操作mongodb了
底层存储引擎剖析(大致知道流程即可):
概述:
存储引擎是MongoDB的核心组件,负责管理数据如何存储在硬盘和内存上
MongoDB支持的存储引擎有MMAPv1,WiredTiger和InMemory
InMemory存储引擎用于将数据只存储在内存中,只将少量的元数据(meta-data)和诊断日志(Diagnostic)存储到硬盘文件中
由于不需要Disk(磁盘)的IO操作,就能获取所需的数据
InMemory存储引擎大幅度降低了数据查询的延迟(Latency)
从mongodb3.2开始默认的存储引擎是WiredTiger,3.2版本之前的默认存储引擎是MMAPv1
mongodb4.x版本不再支持MMAPv1存储引擎
对应的核心配置(了解即可):
storage:
journal:
enabled: true
dbPath: /data/mongo/
directoryPerDB: true
engine: wiredTiger
WiredTiger:
engineConfig:
cacheSizeGB: 2
directoryForIndexes: true
journalCompressor:none (默认snappy)
collectionConfig:
blockCompressor: zlib ( 默认snappy,还可选none、zlib)
indexConfig:
prefixCompression: true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
WiredTiger存储引擎优势:
文档空间分配方式:
WiredTiger使用的是BTree存储,MMAPV1 线性存储 需要Padding
并发级别:
WiredTiger 文档级别锁(并发性能更优秀),MMAPV1引擎使用表级锁
数据压缩:
snappy (默认)) 和 zlib ,相比MMAPV1(无压缩) 空间节省数倍
大概相差15倍,但并不是很准确,因为一般与压缩的算法有关
内存使用:
WiredTiger 可以指定内存的使用大小
而MMAPv1会占用机器的全部内存(会使用,即上限的机器即内存,并不是其他程序不可以使用)
以后说的占用,一般都是可以占用的上限的意思,而不是真正的占用(除非有特殊的说明)
好像虚拟机会真正的占用本机的一些空间
Cache使用:
WT引擎使用了二阶缓存WiredTiger Cache,File System Cache来保证Disk上的数据的最终一致性,而MMAPv1只有journal 日志
综上所述,可以发现WiredTiger比MMAPv1要好的多,所以基本后面的mongodb版本就不支持MMAPv1存储引擎了
WiredTiger引擎包含的文件和作用:
table*.wt:存储各张表的数据
WiredTiger.wt:存储table* 的元数据(元数据,一般是整体来说的数据,如表名,表占用空间大小等等)
WiredTiger.turtle:存储WiredTiger.wt的元数据
WiredTiger.basecfg:存储基本配置信息,与 ConfigServer有关系
WiredTiger.lock:定义锁操作
journal:存储WAL(Write Ahead Log:前置写日志)
WiredTiger存储引擎实现原理:
写请求:
WiredTiger的写操作(增,删,改)会默认写入 Cache,并持久化到 WAL (Write Ahead Log:预写日志)
该日志每60s或Log文件达到2G(一般默认为2g)做一次 checkpoint(也就是持久化,即保存一下,放入对应的文件里 )
可以这样的理解:我们一般玩游戏时,是有保存按钮的,保存后,对应的状态也就会保存,这里的保存一下也是如此
当然我们也可以通过在写入时传入 j:true 的参数强制 journal 文件的同步并产生快照文件,或者说强制写入
writeConcern { w:< value> , j:< boolean> , wtimeout:< number> }
WiredTiger初始化时,恢复至最新的快照状态(得到保存一下的文件,一般是下面的Snapshosts文件,我们也称为快照)
然后再根据WAL恢复数据,保证数据的完整性
这样基本不会怕死机或者重启,当然没保存的自然也可以得到,因为默认写入了WAL ,即一般通过反向的操作返回数据
Cache是基于BTree的
节点是一个page, root page是根节点, internal page是中间索 引节点, leaf page真正存储数据
数据以page为单位读写,WiredTiger采用Copy on write的方式 管理写操作( insert、 update、 delete),也就来一个新的root page
修改操作会先缓存在cache里,持久化时,修改操作不会在原来 的leaf page上进行
而是写入新分配的page,所以每次checkpoint都会 产生一个新的root page
checkpoint流程 :
对所有的table进行一次checkpoint,每个table的checkpoint的 元数据更新至WiredTiger.wt
对WiredTiger.wt进行checkpoint,将该table checkpoint的元 数据更新至临时文件WiredTiger.turtle.set
将WiredTiger.turtle.set重命名为WiredTiger.turtle
上述过程如果中间失败, WiredTiger在下次连接初始化时,首 先将数据恢复至最新的快照状态
然后根据WAL恢复数据,以 保证存储可靠性
假设1 00s的数据, 60s时产生快照,系统启动直接恢复即可, 后面40s的数据通过WAL日志中的时间点进行恢复
至此,对应的流程一般是,我们进行写入,先到内存(Cache)
然后到WAL (预习日志,一般与journal 一起操作,实际上是journal 给数据给WAL 的)
他保存到对应的文件
我们的机器重启或者死机再次开机时,一般会去保存的对应文件里进行再次的操作,使得数据是最新的状态
剩余的则去WAL 里进行操作,使得是最新状态
当然了,内存肯定是第一个的,因为无论什么的操作,基本需要内存,这里也是一样
Journaling:
在数据库宕机时,为保证 MongoDB 中数据的持久性
MongoDB 使用了 Write Ahead Logging 向磁盘上的 journal 文件预先进行写入
除了 journal 日志, MongoDB 还使用检查点( checkpoint)来 保证数据的一致性
当数据库发生宕机时,我们就需要checkpoint 和 journal 文件协作完成数据的恢复工作
1:在数据文件中查找上一个检查点的标识符
2:在 journal 文件中查找标识符对应的记录,所以说journal 也可以称为WAL
3:重做对应记录之后的全部操作
当然了,如果是很小的,或者我们在默认的写入journal 时之前,死机,或者宕机,那么对应的小数据,一般是找不到的
当然,基本上所有的操作,也很难解决这种情况,因为死机是随时的,而保存却必须要时间,中间的过程就会使得数据丢失
只是这里非常快而已,所以也基本不会出现问题,而且就算是小数据的丢失,也是无关紧要的