MongoDB高级介绍
通过上一章博客的学习,基本上明白了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 秒传递一次
/*
心跳检测:
整个集群需要保持一定的通信才能知道哪些节点活着哪些节点挂掉
mongodb节点会向副本集中的其他节点每两秒就会发送一次pings包
如果其他节点在10秒钟之内没有返回就标示为不能访问
每个节点内部都会维护一个状态映射表,表明当前每个节点是什么角色、日志时间戳等关键信息
如果是主节点,除了维护映射表外还需要检查自己能否和集群中内大部分节点通讯
如果不能,则把自己降级为secondary只读节点(即变成从)
数据同步副本集同步分为初始化同步和keep复制
初始化同步指全量从主节点同步数据(也可以叫做全量同步),如果主节点数据量比较大同步时间会比较长
而keep复制指初始化同步过后,节点之间的实时同步一般是增量同步
不是一次全部,而是自己操作的部分,一次全部那么就算全量同步,同步也可以称为复制
初始化同步不只是在第一次才会被触发,有以下两种情况会触发:
1:secondary第一次加入,这个是肯定的
2:secondary落后的数据量超过了oplog的大小,这样也会被全量复制(因为oplog的操作都相当于执行了)
上面的两个情况可能对于数据来说,并不算同步吧,因为落后了顶峰大小,导致移除的操作没有执行
但对于这里的全量来说,差不多就是代表oplog的大小(虽然redis和zookeeper并不是这样的意思)
*/
副本集中的副本节点在主节点挂掉后通过心跳机制检测到后,就会在集群内发起主节点的选举机制
自动选举出一位新的主服务器,从而保证系统可以正常运行
/*
1:主节点负责处理客户端请求,读、写数据,记录在其上所有操作的oplog
2:从节点定期轮询(大概是心跳)主节点获取这些操作,然后对自己的数据副本执行这些操作
从而保证从节点的数据与主节点一致,默认情况下,从节点不支持外部读取,但可以设置
3:仲裁节点不复制数据,仅参与投票,由于它没有访问的压力,比较空闲,因此不容易出故障
由于副本集出现故障的时候,存活的节点(一般这时都是从节点)必须大于副本集节点总数的一半,否则无法选举主节点
也是按照半数的机制,与redis和zookeeper一样,具体原因可以看看78章博客的解释
或者主节点会自动降级为从节点(多一个可能可以选举成功),整个副本集变为只读
因此,增加一个不容易出故障的仲裁节点,可以增加有效选票,降低整个副本集不可用的风险
仲裁节点可多于一个,也就是说只参与投票,不接收复制的数据,也不能成为活跃节点
*/
一个复制集里至少包含一个主节点
如下图中,三个节点,写操作只能在主节点操作
主节点收到数据操作时,会将操作记录在操作日志里
所有的从节点都会在数据同步的过程中,从主节点读取操作日志,从而保证从节点和主节点的数据统一
无仲裁节点 复制集搭建:
伪集群:在一台服务器下,创建N个mongo实例形成的集群
实际上是根据端口来区分,而不是ip,压力集中给一个节点
基本只在测试的时候进行操作,只要是可以指定端口启动的,一般都可以使用伪集群
如mongo,redis(虽然没有指定,但可以修改配置文件来修改端口)
实际上基本所有的服务器都可以操作伪集群,因为他们本质上也就是操作一个端口,除了固定的(可以自己百度找)
opt目录下创建replica_set目录(名称并不做要求):
[root@A /]# mkdir /opt/replica_set #出现A这个操作,可以在81章博客里进行查看
mongo压缩包copy进去,然后解压:
[root@A opt]# cp mongodb-linux-x86_64-4.1.3.tgz /opt/replica_set/
[root@A opt]# cd replica_set/
[root@A replica_set]# tar -zxvf mongodb-linux-x86_64-4.1.3.tgz
[root@A replica_set]# cd mongodb-linux-x86_64-4.1.3
进入mongo目录,创建下面3个配置文件,并修改其内容如下:
[root@A mongodb-linux-x86_64-4.1.3]# vim mongo_37017.conf
# 主节点配置 mongo_37017.conf
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]# vim mongo_37018.conf
# mongo_37018.conf
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]# vim mongo_37019.conf
# mongo_37019.conf
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]# ./bin/mongod -f mongo_37017.conf
[root@A mongodb-linux-x86_64-4.1.3]# ./bin/mongod -f mongo_37018.conf
[root@A mongodb-linux-x86_64-4.1.3]# ./bin/mongod -f mongo_37019.conf
[root@A mongodb-linux-x86_64-4.1.3]# ps -ef | grep mongo
然后进入任意一个节点(我们进入37017),运行如下命令:
[root@A mongodb-linux-x86_64-4.1.3]# ./bin/mongo --port 37017
启动后,我们进行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"}
#上面两个记得要启动,否则报错(可能出现连接不了,连接被拒绝等等错误)
#且_id要不同(这里是1,2),否则也是报错
]
}
rs.initiate(cfg)
#执行完成显示 lagouCluster:SECONDARY>(还没有完全刷新)
#表示当前节点是从节点,稍等一会,回车刷新,37017会变成主节点lagouCluster:PRIMARY>
#这时就可以进行执行show dbs进行查询了
#若这时,你关闭其中的37018,那么再次观看37017时,对应的状态并没有改变
#刷新也是没有用的,随便执行一个命令,那么就会刷新,变成了从
#因为没有从的主,自然也就是从了(因为没有通信,也就降级为从了)
#虽然选举是按照半数机制,但也不是任何时候都使用半数机制,这里就直接根据权重来以及当前启动者
rs.status() #查看节点状态
_id:集群标识
protocolVersion:协议版本
priority:优先级,整数1~1000,越大,优先级越高,默认为1,设置为0则永不会选举成主
如果都没有设置优先级或者优先级一样,则默认为最先连接的节点为主节点(根据连接的时间戳)
通常来说,谁来执行集群的设置,那么他就是主(第一次),但若执行太快,可能就与实际情况不同了(很少见)
但一般也自动修正的
若出现了且没有修正,可以删除对应的数据目录,然后重新操作,直到满足你的第一个为止
实际上并不需要必须规定谁是主,主要看你自己是否有强迫想法
而像redis和zookeeper基本不会出现相同的结果,所以不做考虑,假如出现了相同的
那么基本也是先启动(不是连接)容易变成主节点(相对于第一个来说)
变成节点后,对应的信息会保存好的,一般保存在数据库里面而不是日志里面(删除数据库也就没有了)
所以,就算关闭对应的mongo,下次启动时,对应的状态还是会变成原来的样子
节点的动态增删:
在主节点下进行操作(因为对节点的操作,一般只能由主节点来进行执行)
#增加节点(该节点一般需要是启动的,可能没有启动的也会加上,也有可能会显示报错,所以最好有该地址)
rs.add("192.168.164.128:37019") #添加后,使用rs.status()查看状态,发现多出了一个节点37019(也就是从节点)
#删除slave 节点
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 #mongo连接时,都会执行里面的操作,这时就相当于永久了
#在里面输入rs.slaveOk(),退出保存即可,这时就是永久的了
#这时再次进行db.yi.find() ,发现的确进行了复制(同步)
复制集群的选举机制:
为了保证高可用,在集群当中如果主节点挂掉后,会自动在从节点中选举一个重新做为主节点
我们需要在启动37019,否则单个机器是不会变成主的(没有通信,除非原来就是变成了主)
kill掉37017,37018会自动上位成主节点
当然,可能因为顺序的原因,会使得37019变成主,一般顺序在前,容易变成主,并不是全看优先级(已经操作了启动为主了)
当37017归来,37018会自动让位给37017,因为37017的优先级太高
在MongoDB 中通过在集群配置中的 属性值大小来决定选举谁做为主节点(如priority)
但选举的操作一般都是票数多的成为主,基本都是如此
有仲裁节点 复制集搭建 :
设置arbiterOnly 为 true 表示做为裁判节点用于执行选举操作,该配置下的节点永远不会被选举为主节点和从节点
举例:加入37020,并全部启动
var cfg = { #变量覆盖,实际上mongo自带的客户端的语句在一个窗口中基本是共享的,并不需要一次全部执行
"_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]# cp mongodb-linux-x86_64-4.1.3.tgz /
[root@A opt]# tar -zxvf mongodb-linux-x86_64-4.1.3.tgz
[root@A /]# mv mongodb-linux-x86_64-4.1.3 shard_cluster
[root@A /]# cd shard_cluster
[root@A shard_cluster]# mkdir config
[root@A shard_cluster]# cd config
第一个配置节点:
[root@A config]# vim config-17017.conf
dbpath=/data/mongo/config1
bind_ip=0.0.0.0
port=17017
fork=true
logpath=/data/mongo/logs/config1.log
replSet=configCluster
configsvr=true #表示为操作配置config节点集群,也就是存放数据的节点集群,使得admin库没有
第二个配置节点:
[root@A config]# vim config-17018.conf
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]# mkdir -p /data/mongo/config1
[root@A config]# mkdir -p /data/mongo/config2
[root@A config]# mkdir -p /data/mongo/logs #-p创建多级目录,且跳过相同的
启动(也可以叫做连接客户端)两台mongo,并进入任意节点shell 并添加配置节点集群,注意,要进入admin库操作
#先进行连接(前提需要先启动两个节点)
./bin/mongo --port 17017
#进入admin库(因为没有admin库,操作他的配置文件启动时,会没有对应库)
#一般我们操作分片需要在这个库里面
#一般在数据库里面进行操作,都会影响该库的信息(可能)
use admin
#我经过测试,发现,的确可以不必要创建这个库,因为实际上下面的操作会使得创建admin库
#所以并不需要创建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) #基本上,会有默认的admin,config,local的数据,了解即可
#配置副本集
#如果弄错了,可以将对应的数据库目录删掉,那么基本全部都会消失(也会使得mongo关闭)
配置 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
创建配置文件:
#在前面创建的config目录里面,创建下面三个文件,分别对应于后面的三个配置
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 #这个使得config库没有
shardsvr=true #表示为操作配置shard节点集群,也就是数据分开的集群,这个使得admin库没有
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
创建配置文件:
#在前面创建的config目录里面,创建下面三个文件,分别对应于后面的三个配置
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 路由节点(类似于负载均衡的角色):
创建配置文件:
#在前面创建的config目录里面,创建下面这个文件,对应于后面的那个配置
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 #没有local库了
#configCluster是集群的名称,需要一致,否则后面的启动,可能启动不了
#指定存放持久数据并可以读取数据的对应两个配置config节点服务器,设置这个
#那么该集群也就是路由分片的集群,用来指定你去那里访问
mongos中添加分片节点:
启动路由使用mongos命令,而不是mongo或mongod(他们两个有多种启动方式)
但我们通常使用配置文件的启动,这里使用他们的启动方式会报错:
[root@A shard_cluster]# ./bin/mongos -f config/route-27017.conf
进入路由和普通的节点一样,使用mongo:
[root@A shard_cluster]# ./bin/mongo --port 27017
#查看分片集群的状态用sh对象
sh.status()
#在admin库下操作(实际上也不用在这里面)
use admin
#注意连接字符串的格式拼写,#指定shard1集群名和shard2集群名
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") #可以不用先创建lagou数据库
#指定集合开启分片功能,需要先开启上面的数据库的分片功能
#sh.shardCollection("lagou.emps",{"片键字段名如 _id":索引说明})
sh.shardCollection("lagou.emps",{"name":"hashed"})
#对该集合使用哈希分片,一般都是默认使用范围分片的
#当使用了shardCollection方法时,默认将所有的分片节点加上该集合(没有数据库创建,没有集合创建)
#但这时该集合因为索引的存在,一般添加的数据中,必须要有name属性,否则是添加不了的
#当然其他集合可以,因为其他集合并没有这个索引
#对于路由来说,一般只有集合,才会通过策略给分片节点,虽然并不会全部给,有范围
#即实现同步数据,但也基本只操作数据,因为就是为了数据而分片的
#执行后,若没有对应的数据库或者集合,那么会创建,并加上索引,其中对应的数据库和集合会同步到他的分片副本集中
由于哈希分片并不是完全的平均,即数据很可能明显分配得不均匀,这时就需要用到迁移区块了,而由于默认chunk的大小是64M
若我们插入的数据量不大,估计也不会产生几个chunks,从而很难出现区块的差值,使得不会进行迁移区块
因为chunk迁移需要满足一定的条件:
虽然使用的是哈希分片,但并不会是完全的平均,这时就需要用到迁移了
当分片集群中,各个分片的副本集中,块的数量有差值,根据上面的相差值来进行迁移
所以,为了能够测试看的很清楚,我们调整下chunk的大小为1M:
#修改分片大小:
use config #需要在这里面进行设置(虽然很难看到效果)
db.settings.save({_id:"chunksize",value:1})
db.settings.find()
插入数据测试 :
通过路由(在路由节点下操作)循环向集合中添加数据
use lagou #规定了是这里面操作分片,是上面的设置的原因
for(var i = 1 ; i <= 1000 ; i++){
db.emps.insert({"_id":i,"name":"test"+i})
#加入的数据根据分片规则来给对应的分片区块(并不会看节点ip),只是他用来保存的而已
#一般只会操作主的块(因为会同步给从)
#执行后,查看数据find()方法,虽然只会显示其中的20条数据(分页的查询,默认20个)
#但这时却不能再次添加相同的对应索引的字段数据了(操作了策略,自带特殊唯一,不是完全的唯一,后面的相同介绍)
#注意:必须在开启了分片的那个集合里面进行操作,且必须有name字段(因为他操作了分片策略)
#前者不会将数据给副本集,后者会使得报错
#其中路由一般会保存其顺序的数据,当路由所对应的分片都有该数据时,那么添加一样的会报错(特殊唯一)
#除非他们其中某个没有,那么就会出现覆盖添加(顺序相同的地方覆盖,其他相同报错,但任然是不同的添加)
#正是因为哈希算法,所以可能会出现多次删除添加,都是同一个位置
}
测试:
在shard1和shard2分片中,分别执行如下,观察结果
发现的确进行了分区块操作,将数据根据策略分发给各个副本集,也就是分片节点
注意:若中途宕机一个,或者说连接不了,那么分片可能不会进行,所以对应的分片一般是集群,那么可以继续分片
当然若都进行了宕机,那么可能就不会执行分片了,即可能添加不了
当然可能也在添加,若有添加,那么就有两种情况
第一:将没有宕机的进行分片策略的分片
第二:假装他没有宕机,让他参与分片策略,只是分给他的数据存不了而已
一般情况下,是第二种,因为动态的改变设置的集群地址,是很难的,就算可以,也只是拿性能换取数据,具体看当时的情况,如何操作可以百度
而读取分片集群,一般是进行全部依次读取累加显示(通常是默认操作这个的),因为总不能将查询的数据先保存再给前端吧(虽然大多数是先保存然后再给前端),自然是依次的给,但有时也只会读取其中少部分分片节点(一般在数据量特别大的时候操作的,甚至可能只读取一个)
所以整体看来,我们就可以将分片集群(虽然可能还会让分片集群)看成一个机器即可
安全认证:
安全认证概述
MongoDB 默认是没有账号的,可以直接连接,无须身份验证
实际项目中肯定是要权限验证的,否则后果不堪设想
从2016年开始发生了多起MongoDB黑客赎金事件,大部分MongoDB安全问题暴露出的短板其实是用户
首先用户对于数据库的安全不重视,其次用户在使用过程中可能没有养成定期备份的好习惯
最后是企业可能缺乏有经验和技术的专业人员,所以对MongoDB进行安全认证是必须要做的
用户相关操作 :
切换到admin数据库对用户的添加
创建 MongoDB 登录用户以及分配权限的方法:
#举例子:
db.createUser({
user : "账号",
pwd : "密码",
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 #将原来的21017修改成27020了
bind_ip=0.0.0.0
fork=true
logpath=/data/mongo/MongoDB.log
logappend=true
auth=false #若不写,那么也是默认false的
#根据上面这个配置文件启动后
#进入该库(虽然操作该库,但也可以是其他库)
use admin
#账号是跟着库走的,后续的操作(如修改密码,删除用户,验证用户等等)一般都要在这个库里面(绑定了账号的库)
#否则执行不了,即报错(除非那个库也绑定了下面的信息),因为信息在该库里
db.createUser({
user : "laosun",
pwd : "123123",
roles : [
{ role:"root" , db:"admin" }
]
})
#若添加过了,再次添加会报错(说明已存在already exists)
修改密码:
db.changeUserPassword('laosun','123321') #用户名需要一致
用户添加角色 :
#举例子(后面会说明对应的角色和权限):
db.grantRolesToUser('用户名', [{ role: '角色名', db: '数据库名'}]) #给该用户加上对应的角色,相同的则覆盖
#如db.grantRolesToUser('laosun', [{ role: 'root', db: 'admin'}]),给laosun加上对应admin的root角色权限
#好像对应的角色并不是任意的数据库都可以加的,如root只在admin数据库中可用,若是其他数据库,则会报错
关闭mongo,以安全认证的方式启动mongo
以auth 方向启动mongod:
./bin/mongod -f mongo.conf --auth #一般auth值默认是false的
#由于配置文件里面设置的是false,即不使用账号密码登录,加上了--auth相当于使用账号密码登录(优先配置)
#假设没有设置,那么一般有默认的用户名或者密码,若没有默认的,那么就操作不了了
也可以在mongo.conf 中添加auth=true 参数
输入如下(验证用户):
db.auth("账号","密码") #db.auth("laosun","123123")
#验证好后,那么我们就是这个角色了,这时就可以操作了(对所有执行都可以操作,因为是root)
删除用户:
db.dropUser("用户名") #db.dropUser("laosun")
角色:
数据库内置的权限 :
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)
#每次的启动mongo时,日志中都会给出他的端口号信息(可以看到的显示)
use admin
db.createUser({
user:"root",
pwd:"123123",
roles:[{role:"root",db:"admin"}]
})
#若当前数据库创建过该用户(root),那么会提示已存在,其他数据库可以创建一样的(数据库是不同的标识)
#且验证登录时,只会验证当前数据库的用户
创建普通用户 :
如下所示 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 #要执行下面的操作,需要在admin库里面进行操作
db.shutdownServer() #执行后,ctrl+c退出,使用ps -ef | grep mongo 发现的确没有了对应的mongo的服务
MongoDB 安全认证方式启动 :
修改对应的配置文件:
dbpath=/data/mongo/
port=27020 #将原来的21017修改成27020了
bind_ip=0.0.0.0
fork=true
logpath=/data/mongo/MongoDB.log
logappend=true
auth=true #将原来的false修改成true,这样就不用在启动时指定--auth了
直接启动即可,不用指定–auth了
分别以普通用户和管理员登录验证权限:
普通用户现在仍然像以前一样进行登录,如下所示直接登录进入 mydb 数据库中,登录是成功的
只是登录后日志少了很多东西(因为是使用了auth为true的登录,而auth为false则要多很多,默认的)
而且执行 show dbs 命令,以及 show tables 等命令都是失败的
即使没有被安全认证的数据库,用户同样操作不了,这都是因为权限不足
一句话:用户只能在自己权限范围内的数据库中进行操作
执行show dbs出现错误:
如下所示,登录之后必须使用 db.auth(“账号”,“密码”) 方法进行安全认证,认证通过,才能进行权限范围内的操作
show dbs 已经看不到其它未拥有操作权限的数据库了,显然它也操作不了其它未授权的数据库
因为创建的用户 zhangSan 拥有对 mydb 的读写权限,所有我们进行登录验证,使得我们得到权限
#db.auth("账号","密码") 返回 1 表示验证成功,0 表示验证失败
db.auth("shangSan","123123")
#这时进行操作读取(但这里只有当前数据库的权限)
上面在mydb里面的集合a里创建了一个数据,并读取了
注意:当你进行多次验证时会出现问题:
#若这时,我们切换到admin里面(同一个数据库里,不会出现问题,只是用户角色切换了)
#再次进行验证,然后回来,再次读取,会出现如下:
如上错误码所示,是因为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]# ./bin/mongo localhost:27020/mydb -u "zhangSan" -p "123123"
#这样就不需要连接后,在里面进行验证了,相当于use mydb和db.auth("zhangSan","123123"),他们两个依次执行
客户端管理员登录如下所示 管理员 root 登录,安全认证通过后,拥有对所有数据库的所有权限
use admin
db.auth("root","123123")
上面是操作一个机器的,现在我们操作多个机器
分片集群认证 :
认证之前 进入路由 :
[root@A shard_cluster]# ./bin/mongo --port 27017
创建管理员:
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"}]
})
关闭所有的节点:
如配置节点、分片节点、路由节点
一个一个关闭太累了,搞一个小工具,可以快速关闭进程
#先使用ctrl+c或者exit退出客户端,然后操作如下
[root@A /]# yum install psmisc
[root@A /]# killall mongod #杀死所有关于mongod的进程,大概是后缀
[root@A /]# killall mongo #杀死所有关于mongo的进程,大概是后缀
[root@A /]# killall mongos #杀死所有关于mongos的进程,大概是后缀
#这三个,使得可以完全的操作关于mongo的进程的杀死
#为什么我会说是后缀呢:
#看如下图片:
其中,我们可以发现,有两个不同的文件,./bin/mongod和./bin/mongos,后缀是mongod和mongos,对应的文件是启动的开始
而killall后面指定的就是文件的最后一个后缀(或者是完整名)
所以说上面的组合基本上可以将全部的mongo进程杀死,当然你可以进行其他测试
但也要注意:有些是需要强制的杀死的,否则一般会再次出现(一般是父进程的原因)
如使用killall -9 后面接后缀,进行强制杀死
生成密钥文件 并修改权限:
密钥文件是各个节点直接通信的凭证(通关文牒)
另一种通关文牒:509数字证书(百度自行查阅)
赋予权限只能是600
该文件的权限大概率会被检查,若不是600
可能根据该配置文件启动时,会启动不了(我测试过,的确启动不了,报错了)
也有可能某些操作中,会报错,甚至会执行不了,因为这里是密码所在的地方
[root@A /]# mkdir -p /data/mongodb
[root@A /]# openssl rand -base64 756 > /data/mongodb/testKeyFile.file
#将返回值写入对应的文件里面,若没有文件,会自动创建
[root@A /]# chmod 600 /data/mongodb/testKeyFile.file
OpenSSL简介:
在所有的类 Unix 发行版、Solaris、Mac OS X 和 Windows 中
默认都用openssl这个工具来生成高强度随机密码(这个是系统自带,使用率最高)
[root@A ~]# openssl rand 6 -base64
#使用openssl生成随机数6位的高强度密码(-base64加密),自然-base64和6是可以换位置的(因为"-")
#返回vaJgqrLe(我这里返回的)
注:上面的命令将生成一个随机的、长度为 6 个字符的高强度密码,这种方式不支持同时生成多个密码
我们强烈推荐你生成 14 个字符的密码,当然你可以使用 OpenSSL 生成任意长度的密码
不是说6位长度吗,怎么显示的不是6位,因为你生成的位数不足以达到高强度密码要求
所以默认情况下openssl会给你添加字符,使其保证密码高强度(这也使得无论你多少位,基本都会有添加)
添加规则:
#从随机数1开始(0代表0位的密码,不会显示,或者说不操作,返回空),对应密码从4位开始
#每三个随机数,对应密码位加4,举例:
#随机数1,2,3,代表4位密码
#随机数4,5,6.代表8位密码
#随机数7,8,9,代表12位密码
#以此类推当随机数是26时,由于26在25,26,27中,由于27/3=9,代表了第9个三个随机数位置,那么密码位是4*9=36位
#可以自己进行测试,我经过测试,的确是36位(可能会与Linux镜像有关系,但大多数都是如此)
配置节点和分片节点 设置密钥文件(太多了自己慢慢加吧(●ˇ∀ˇ●)) :
向 对应的xxx.conf 文件中追加如下内容:
auth=true
keyFile=/data/mongodb/testKeyFile.file
路由节点 设置密钥文件 (使得对应的相同密码的可以被路由操作,不同的基本不能加入集群,即不会被操作,大概是):
切记:路由不需要认证
keyFile=/data/mongodb/testKeyFile.file
启动所有节点:
挨个启动太累,可以编写一个shell 脚本批量启动
[root@A /]# cd /shard_cluster
[root@A shard_cluster]# vim startup.sh #实际上可以不是.sh后缀的,只要是文件都可,只是.sh好区分
[root@A shard_cluster]# chmod +x startup.sh #添加执行文件权限
[root@A shard_cluster]# ./startup.sh #执行前,先在里面加上下面的文件内容
#执行里面的命令(实际上我们命令行就是相当于在一个文件里,始终的添加执行的)
[root@A shard_cluster]# ps -ef | grep mongo #查看进程
下面内容拷贝到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
#添加好后,保存退出
#执行./startup.sh,那么对应的就都启动了
认证测试:
进入路由节点(测试管理账号)
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章博客的内容,那么这里你就知道是怎么回事了:
#不要使用root,使用普通用户,否则报错,认证失败
spring.data.mongodb.username=账号
spring.data.mongodb.password=密码
# uri方式:注意帐号密码在@前面
# spring.data.mongodb.uri=mongodb://账号:密码@IP:端口/数据库名
#注意:上面是操作连接mongo的方式
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]# tar -zxvf mongodb-mms-4.1.3.53428.20190304T2149Z-1.x86_64.tar.gz
解压完成后改个名字,名字太长了:
root@A opt]# mv mongodb-mms-4.1.3.53428.20190304T2149Z-1.x86_64 mongodb-mms
编辑配置文件(conf-mms.properties):
[root@A opt]# cd mongodb-mms/conf
[root@A conf]# vim conf-mms.properties
根据自己的mongodb 进行配置,修改该文件的对应的地址端口为27020(因为我们正好有个单机的27020进行测试)
操作时记得需要先启动一个27020的mongdb实例
mongo.mongoUri=mongodb://127.0.0.1:27020/?maxPoolSize=150 #连接的mongo地址,以及线程池容量
#用该mongodb数据库作为支持运行的数据库
#若对应的实例没有启动,一般这里也就启动不了,即会报错
mongo.ssl=false #是否开启认证,这里就是不开启(默认不开启)
#上面是该文件里面默认的,当然可以进行修改
#原来是如下
mongo.mongoUri=mongodb://127.0.0.1:27017/?maxPoolSize=150
mongo.ssl=false
当前目录的mms.conf:
[root@A conf]# vim mms.conf
BASE_PORT=8080 #连接web项目的接口
BASE_SSL_PORT=8443
# JVM configurations
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]# ./bin/mongodb-mms start
#当然若要进行停止(关闭,只需要将start修改成stop即可),即./bin/mongodb-mms stop
启动时间漫长,多等一会,如果出现以下的信息:
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]# mkdir /opt/mongodb-mms/versions
#我们将上面图片中对应的参数修改成/opt/mongodb-mms/versions即可
#这样好见名知意一点,虽然你也可以创建上面的目录
至此添加后,再次点击下一步,发现,注册成功,并自动的给你登录了,如出现如下,则代表操作成功:
配置 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
#注意:rpm这个命令需要在上面的目录下(因为需要上面下载的文件),且需要root用户,普通用户要想操作,需要sudo命令
#root用户可以不使用sudo,使用sudo是为了让普通用户可以操作一些或者全部的root用户的操作
#虽然普通用户操作sudo时也需要对应的权限,当然百度上自然有解决方案,这里就不说明了
接下来我们点击生成密钥,会发现下面的这里会出现数据:
生成后,以后再次进入,就基本没有该按钮了,需要点击代理API密钥进行查看:
假设你忘记了,那么可以进行删除,那么生成密钥的按钮就会重新出现,只是可能会有如下提示,当然现在可以无视
接下来继续跟着下一步:
sudo vi /etc/mongodb-mms/automation-agent.config
#修改对应的参数值或者添加上
mmsGroupId=62ec7e73c8042ae42592d959
mmsApiKey=62ec8dd5c8042ae42592e2c211463a7380314c1b6120f059b4b17474
#当然,若对应的密钥删除了,那么大概使用该密钥的所有操作,基本都访问不了对应的mongo的,就如上面的提示一样
#所以也最好不要中途删除,防止数据错误或者出现提示错误
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]# ./bin/mongodump -h 192.168.164.128:27020 -d test -o /data/bdatas
#代表将192.168.164.128:27020地址的mongo的test数据库备份到/data/bdatas文件里面
[root@a mongodb]# ./bin/mongodump --host=192.168.164.128 --port=27020 -d test -c emps -o=/data/emps_bak
#代表将192.168.164.128:27020地址的mongo的test数据库的emps集合备份到/data/emps_bak文件里面
#如果/data/emps_bak目录不存在,则进行创建,当然不能有已经存在的文件(不是目录),否则会报错
#若数据不存在,自然不会备份,也就什么都不操作,后面的方式一般会报错
#前面的不会(只是什么都不操作而已,后面的方式也是一样的)
#当然,-的位置是可以变化的,比如:
./bin/mongodump --host=192.168.164.128 --port=27020 -c emps -d test -o=/data/emps_bak
#-c和-d对应的值位置变化,并不会影响,具体为什么,可以看看54章博客
导出的数据目录中:一个数据库对应一个目录
即根据上面的例子,知道/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 选项
举例:
#下面的mongodb在一个mongo数据库里进行测试(这里一般操作对应的27020的那个)
[root@a mongodb]# ./bin/mongorestore -h 127.0.0.1:27020 -d test /data/emps_bak/test
#将/data/emps_bak/test对应的数据库目录里面的文件恢复到test目录
#如果没有test目录会创建,并创建对应的集合,在恢复数据,如果有,自然是直接恢复
[root@a mongodb]# ./bin/mongorestore -h 127.0.0.1:27020 /data/bdatas #当然对应的目录需要存在,否则报错
#正是因为会创建,所以我们可以直接的指定备份目录所在文件夹
#使用他对应的目录为数据库和集合,从而使得都恢复(我们也可以称为全量恢复)
#而加上-d,那么需要指定数据库目录,而不能指定对应的数据库存放目录了
#因为不加-d是使用对应的目录为数据库名称,与-d冲突
#注意:这里可以指定192.168.164.128,当然由于是本机操作,所以可以指定127.0.0.1
#否则一般与备份一样,都是远程的操作
#但也要注意一定,若多次的备份,一般都是覆盖,但恢复,一般第二次会报错,因为恢复过了
#主要看恢复的那个_id是否存在,若存在,则报错,不存在,则恢复(隐藏的集合则覆盖,不看_id,如admin)
#之所以根据_id来操作,是因为_id基本是不能修改的(无论是否操作图形化界面)
如果指定-d参数,则数据目录必须指定到库目录
如果没指定库的目录,那么-d参数也不可以指定
总之:-d和库目录test要么同时存在,要么同时删除
全量加增量备份和恢复案例:
注意的问题:
这里我们使用前面操作的复制集的mongo(可以看看看本章最前面的那个)
删除复制集中原来的数据文件目录/data/mongo/data/server1
重新建立数据目录data
重新启动复制集中的实例,进入37017重新进行复制集的配置
#记得对应的ip与你的一样
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) #一般会导致local多出一些数据,如oplog.rs集合(后面的增量操作时用到了)
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]中的mongodb
#这个一般在前面的mongo(replica_set里面的mongo里面,即mongodb-linux-x86_64-4.1.3里面)里进行测试
#该mongo这里是统称,一般是对应的mongodb的文件夹
[root@a mongodb]# rm -rf fullbackup/ oplog_bak/ #若当前mongo目录下没有这两个文件,则不需要删除
[root@a mongodb]# ./bin/mongodump --host=192.168.164.128 --port=37017 --out=/mongo/fullbackup
#不指定数据库,那么就是全部的数据库进行备份,就如都恢复的那个语句类似,或者说,该mongo数据都进行了备份
#当然,有些数据库不能被备份(大概是数据库的原因吧),如config不可以备份,而admin和local却可以
#但是,操作全部的备份,local也不可以,他只能是操作指定数据库(如-d local),而config怎样都备份不了
#向这种进行全部数据库备份的,一般称为全量备份
继续插入数据 并更新 :
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]# ./bin/mongodump --host=192.168.164.128 --port=37017 -d local -c oplog.rs -o=/mongo/oplog_bak
#你会发现,增量备份(对于的恢复也可以说是增量恢复)不就是普通的备份吗(实际上全量备份也是)
#实际上增量备份是相对普通备份来说的
#即对于前面的备份的操作后的备份,即相对于前面的备份的增量,简称为增量备份
#这里虽然操作的是时间点上的备份以及恢复,当然也可以称作为增量
#全量备份,也就是全部的备份
#实际上无论是全量还是增量
#他们的操作都是备份或者恢复的操作,只是因为操作全量(全量备份,全量恢复)和相对于的操作(增量备份,增量恢复)
#而形成的名称而已
删除所有的数据:
use laosundb
db.emps.remove({})
db.emps.find()
先恢复全量数据:
[root@a mongodb]# ./bin/mongorestore --host=192.168.164.128 --port=37017 --dir=/mongo/fullbackup
use laosundb
db.emps.find()
#此时,恢复了前两条数据
恢复数据到指定的时间点
修改数据备份文件:
oplog.rs.bson -> oplog.bson (去掉中间的rs),并且删除meta元数据文件
[root@a ~]# cd /mongo/oplog_bak/local
[root@a local]# mv oplog.rs.bson oplog.bson
[root@a local]# rm -rf oplog.rs.metadata.json
一般需要上面的这样操作,否则可能不会恢复(报错)
找出第一次更新的时间:
对应的数据里面包括了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]# ./bin/mongorestore --host=192.168.164.128 --port=37017 --oplogReplay --oplogLimit "实际查询出来的时间" /mongo/oplog_bak/local
#实际查询出来的时间 = Timestamp(1659751592, 5)
#这个数据是操作上面的db.oplog.rs.find({"op" : "u"}).sort({"ts":1})得到的
root@a mongodb]# ./bin/mongorestore --host=192.168.164.128 --port=37017 --oplogReplay --oplogLimit "1659751592:5" /mongo/oplog_bak/local
#每次的操作会将产生的新的记录放入到对应的local里的对应文件里面(规定只能有oplog.bson文件,否则报错)
#从而使得我们可以恢复,但是,若对应的文件里面没有对应的时间点的话,那么相当于什么都不操作
#但是对应的文件数据并不能删除,因为里面有确定的新记录存放位置的,否则以后操作时,基本恢复不了了,即什么都不操作
#相当于他是一个必须要的日志文件,或者回滚文件
#在特殊的情况下,可能有缓存,导致会变成缓存操作的数据(但并不需要理会,继续操作回来即可)
#--oplogReplay回放的意思,--oplogLimit从那个日志数据回放,一般这两个用来操作local的
#因为每次的修改数据,对应的local数据库的对应的集合就会有数据,一般是日志,即oplog.rs数据
#执行后,对应修改的那个数据会回到原来没有修改过的(无论是否相同,即可以多次运行)
#换言之,对应的db.oplog.rs.find({"op" : "u"}).sort({"ts":1})记录了你每次修改后之前的数据(或者说状态)
#基本只有修改才会进行记录,增加和删除和查询不会,那么当你修改了上面的444时
#他就记录了对应的修改之前的数据(或者说状态)
#这时若操作他的对应的时间,那么就回到了没有修改之前的数据(或者说状态)
#这时又会出现一个记录,因为你的恢复也会有对应的数据出现,当然,若你操作你恢复的记录
#一般会根据当前和之前的数据添加对应的几条记录(顺序),若再次操作,那么就会根据顺序多出,直到没有
#且不是修改之前的数据,而是保存恢复的数据
#看不懂没关系,可以看如下的图片:
我的例子:
出现的三个中,只要使用妻子一个,一般就会根据顺序出现相应的记录
具体可以自己进行测试,可能这有些问题,虽然我测试的结果是这样,但你还是继续测试一下吧
查看数据恢复:
use laosundb
db.emps.find()
#看看是否回到了对应的状态
定时备份:
创建备份数据的存放目录
mongo_bak_now:数据备份的临时目录
mongo_bak_list:备份数据压缩文件的存放目录
[root@a /]# mkdir -p /mongo/backup/mongo_bak/mongo_bak_now /mongo/backup/mongo_bak/mongo_bak_list
编写备份脚本:
[root@a /]# vi /mongo/backup/mongobk.sh
对应的mongobk.sh数据:
#!/bin/sh
# dump 命令执行路径,根据mongodb安装路径而定
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`
# 数据库账号
#DB_USER=user
# 数据库密码
#DB_PASS=password
# 代表删除7天前的备份,即只保留近 7 天的备份
DAYS=7
# 最终保存的数据库备份文件名称,例如 mongod_bak_2020_08_02_10_20_35.tar.gz
TAR_BAK="mongod_bak_$DATE.tar.gz"
#上面都定义好了,接下来编写执行步骤(以前说过,这个执行,也就相当于命令行的执行,命令行也相当于一个sh文件)
#55章博客里面有说明,这里必须要理解,理解后,那么下面的代码就非常简单了
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.gz 格式
#这里先给出一个问题,如果是毫秒级别,那么这里会有相同名称的文件吗,答:不会,如果有,那么就是覆盖
#但是一般来说,对应的循环最低也是1分钟执行该脚本一次(如后面的定时任务说明),所以也基本不会出现这种情况
tar -zPcvf $TAR_DIR/$TAR_BAK $OUT_DIR/$DATE
# 删除 7 天前的备份文件
find $TAR_DIR/ -mtime +$DAYS -delete
exit #这个在远程的连接中,一般默认是退出连接,在窗口中,默认退出窗口
#在脚本里算是退出执行,脚本一般有对应的退出返回
#我们可以发现,脚本的作用非常大,一般我们在工作时,若有重复的操作,就可以使用脚本来进行
#正是因为命令行也是相当于sh文件,所以在命令行输入aa=1,echo $aa,会返回1,当然有些有特殊作用
#如echo &ss,查看对应的端口信息,当然还有很多,也就不依次举例了
#当然,这是一行一行的运行,不会抢占(打印的信息最后显示)
修改脚本权限:
[root@a mongodb]# chmod +x /mongo/backup/mongobk.sh
编辑crontab:
[root@a mongodb]# crontab -e
#代表编辑一个定时任务,crontab一般是linux自带的命令,一般用来循环操作某个文件,如循环操作脚本
#表示每天凌晨2点30执行脚本,使得备份
30 2 * * * /mongo/backup/mongobk.sh
#对应的*代表的顺序是,分钟,小时,天数,月份,第几周等等
#所以30 2 * * *代表2小时,30分钟,*相当于是所有,也就是所有的月份,天数,以及周,只要是到了2小时,30分钟就执行
#也就是每天的2小时,30分钟执行
#即只要到了这个时间,他就会进行执行后面的文件(一般也就是脚本)
#我们可以发现,没有年份,实际上年份的操作基本不会进行,因为基本不会有相同的年份,所以也就移除了
测试的时候 可以改成 一分钟备份一次:
* * * * * /mongo/backup/mongobk.sh #全部是*,则默认1分钟执行一次
#当然,我们每次的编辑后,在没有操作过定时任务的手动启动或者关闭时
#一般会默认执行一次,然后根据时间来进行循环执行
#若操作过或者执行过,那么基本不会默认执行了
#一般中途删除的话,那么一般对应的判断也就停止了
#该文件会每次来判断的,从而使得执行,要不然你以为他真的的根据时间来的吗
#所以说,中途的删除,就代表对应的执行不会操作,删除方法有对应的crontab -r或者手动的crontab -e来进行删除
#注意:* * * * *是最低的,默认是1 * * * *,一般对应的值不能是0或者负数,否则不会执行,即最低是1
查看crontab 的状态:
[root@a mongodb]# service crond status
这时,到对应的目录下,看看是否有文件出现,若有,则代表操作成功
如果没有启动,可以使用下面的命令启动定时服务和加入开机自启动(因为需要完全的自动操作,也是需要开机自启的):
# 启动定时任务,默认是启动的
[root@a mongodb]# service crond start
# 关闭定时任务
[root@a mongodb]# service crond stop
# 加入开机自动启动(需要在启动定时任务的情况下,否则这个命令一般会报错)
#设定crond在等级3和5为开机运行服务(系统服务分为0-6个级别),on表示启动
#下面指定第3级别到第5级别执行该命令,具体级别作用可以百度,一般设置为35(即是3到5的级别,包括3和5)
[root@a mongodb]# chkconfig --level 35 crond on #一般默认是不启动的
查看定时任务和删除定时任务:
[root@a mongodb]# crontab -l # 查看当前定时任务 list
[root@a mongodb]# crontab -r # 删除定时任务 remove,相当于编辑时在底行模式下使用%d来删除全部数据
[root@a mongodb]# crontab -e # 编辑定时任务 edit
至此,只要有这个备份,那么一般来说,基本就可以随意的操作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
## WT(wiredTiger)引擎配置
WiredTiger:
engineConfig:
##WT最大使用cache,即最大使用内存(根据服务器实际情况调节)
cacheSizeGB: 2 #2g内存
##是否将索引也按数据库名单独存储
directoryForIndexes: true
journalCompressor:none (默认snappy)
##表压缩配置
collectionConfig:
blockCompressor: zlib (默认snappy,还可选none、zlib)
##索引配置
indexConfig:
prefixCompression: true
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 时之前,死机,或者宕机,那么对应的小数据,一般是找不到的
当然,基本上所有的操作,也很难解决这种情况,因为死机是随时的,而保存却必须要时间,中间的过程就会使得数据丢失
只是这里非常快而已,所以也基本不会出现问题,而且就算是小数据的丢失,也是无关紧要的
版权声明:本文为qq_59609098原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。