mongodb基础整理篇————副本原理篇[外篇]

前言

简单介绍一下副本集的原理篇。

正文

下面是几个基本的原理:

副本之间是如何复制的?

mongodb 实现此功能的方式是保存操作日志,其中包含了主节点执行的每一次操作,这和mysql比较像。

oplog 是存在于主节点local数据库中的一个固定集合。从节点通过查询此集合以获取需要复制的操作。

每个从节点都维护自己的oplog,用来记录它从主节点复制的每一个操作。

这使得每个成员都可以被用作其他成员的同步源。

从节点从同步源获取操作,将其应用到自己的数据集上,然后再写入oplog。

如果一个从节点由于某种原因而停止运行,那么当它重新启动后,就会从oplog中的最后一个操作开始同步。

由于这些操作是先应用到数据上,然后再写入到oplog,因此从节点可能会重复已经应用到其数据上的操作。

mongodb 在设计的时候就考虑到了这种情况:将oplog中的一个操作多次与只执行一次的效果一样的。

oplog中的每个操作都是幂等的。

一般来说主节点每分钟写入1kb的数据,那么oplog也是每分钟1kb数据。

但是如果是删除的话,那就不一样,比如说用户删除10000文档,那么oplog 可能就有10000条记录了,而不是oplog产生一条记录。

这样做是为了保障oplog 执行的幂等。

因为opsize 也是有大小的,不可能无限的进行扩长。

这个opsize 设置非常重要,比如说,如果opsize 设置太小的话,那么就一个问题,从节点来不及同步的话而oplog 被删除的话,那么同步就会中断。

在mongod 进程创建oplog 之前,可以使用oplogSizeMB 选项指定其大小。然后,在第一次启动副本集成员后,只能使用"更改oplog大小" 这个流程来更改oplog的大小的。

初始化同步

一般来说,如果一个副本加入到副本集,他就会检查自身状态,如果没有问题,那么就会进行初始化同步。

首先mongodb 会克隆除local数据库之外的所以数据库。mongod 会扫描源数据中的每个集合,并将所以数据插入目标成员上这些集合的对应副本。

在开始克隆操作之前,目标成员上的任何现有数据都将会被删除。

在克隆过程将会将同步源变得缓慢:某些数据的子集经常被访问,因而这部分数据总是存在内存中。

执行初始化同步会强制将其所有数据分页加载到内存中,从而驱逐那些经常使用的数据。原本是可以通过在ram访问的数据,现在要在磁盘上访问自然就慢了。

然后如果数据库很大的话,那么其实是通过备份数据库中来还原数据库,而不是从初始化中来同步。

如果一个从节点远远落后于同步源当前的操作,那么这个节点就是过时的。

那么这个节点会怎么做呢?这个节点会找一个拥有足够长的oplog的节点进行同步,如果找不到,那么就要重新初始化或者从备份中恢复。

为了避免这种情况,那么oplog 必须覆盖两到三天的正常操作。

客户端使用副本集的一些注意点

客户端我们叫做驱动程序。

驱动程序和mongodb 进行写操作的时候,因为是一个网络传输,那么如果保证网络传输的一个幂等性呢?

一般来说,没有网络问题的情况下,直接成功了,这样是没有问题的。

那么有下面几种情况: 1. 服务短暂不可以 2. 服务长时间不可用 3. 服务命令错误,返回错误码

3 这种情况下肯定不进行重试的了。

1 和 2是我们驱动程序不知道,到底是1还是2呢? 那么怎么样的重试策略是好的呢?

如果出现了服务不可用,那么可能就是在故障转移,这个时候在重新进行选举。

所以驱动程序,采用至多重试一次的策略,就是在服务不可用的情况下,等待一个选举时间(估计的时间),然后进行重试。

当然这里有人会提出疑问,万一第一次成功了,第二次运行这样不就有问题了吗?

这个是不用担心的,mongo 做了幂等的。

在写入时等待复制

这个跟kafka 有点像。

是这样的机制,就是在副本集写入的时候,可以设置一些选项,比如大部分副本集同步到了,才返回成功结果。

db.products.insertOne({
  "_id":0,
   "item":"envelopes"
},
{
   "writeConcern":
    {
      "w":"majority",
      "wtimeout":100
    }
})

这个writeConcern中的w设置副本集同步多少个。

w 可以是数字,2表示同步了一个从节点。

这里面还可以自定义规则,没用到过,所以这里就不介绍了。

什么时候用副本集从节点读取

这个问题前面也提及过,因为从节点是存在延迟的,如果不介意延迟的话,那么是可以的。

同样,如果考虑到负载情况,从从节点读取的确可以负债压力。

但是如果有你需要负载30000次每秒的读取,但是你主节点只有10000次。

你采用这种方式的话,那么你可以需要4台机器,3台有选举权,1台没有。

但是如果有一台挂了,那么每台机器负荷就是100%,如果有一台出现了问题,需要重新同步数据,那么负荷就会超过100%。

所以这个时候还是建议分片。

维护相关

单机维护

如果希望维护某个从节点,但是又不希望在主节点上维护,那么可以采用单机维护。

使用 db.serverCmdLineOpts() 查看启动命令:

mydb:SECONDARY> db.serverCmdLineOpts()
{
	"argv" : [
		"mongod",
		"--replSet",
		"mydb",
		"--dbpath",
		"/root/data/rs2",
		"--logpath",
		"/root/logs/rs2/log",
		"--port",
		"27018",
		"--smallfiles",
		"--oplogSize=200"
	],
	"parsed" : {
		"net" : {
			"port" : 27018
		},
		"replication" : {
			"oplogSizeMB" : 200,
			"replSet" : "mydb"
		},
		"storage" : {
			"dbPath" : "/root/data/rs2",
			"mmapv1" : {
				"smallFiles" : true
			}
		},
		"systemLog" : {
			"destination" : "file",
			"path" : "/root/logs/rs2/log"
		}
	},
	"ok" : 1,
	"operationTime" : Timestamp(1665935221, 1),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1665935221, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}

然后将该服务停止。

db.shutdownServer();

然后再换另外一个端口启动:

mongod --port 30000 -dbpath /root/data/rs2

这个就相当于这个从节点脱机维护了。

控制成员状态

如果希望某个主节点不再为主节点:

rs.stepDown(600)

这样主节点就会变成从节点。

如果希望主节点进行维护,同样维护阶段不希望其他从节点成为新的主节点:

其他节点运行:

rs.freeze(10000)

当维护结束,运行:

rs.freeze(0)

副本是如何选择从哪里复制的呢?

一般情况下由系统自动选择。

那么是否可以手动,也是可以的:

secondary.adminCommand({"replSetSyncFrom":"localhost:27018"})

这样就可以设置。

还有一点比较重要的是,要设置这个oplog大小。

这个是很关键的,如果oplog不够,那么很有可能复制就会中断了。

查看当前oplog 大小。

那么需要我们设置一下。

db.adminCommand({"replSetResizeOplog":1,size:16000})

这个就是16G了。

下一节分片。

posted @ 2022-10-17 00:04  敖毛毛  阅读(161)  评论(0编辑  收藏  举报