【Python基础】mongodb副本集复制原理是什么

2021-01-21 15:53发布

3条回答
爱搞事的IT小男孩
2楼 · 2021-01-22 10:15

Secondary初次同步数据时,会先进行init sync 操作,从Primary(或其它数据更新的Secondary)同步全量数据,然后不断通过tailable cursor从Primary的local.oplog.rs集合里查询最新的oplog并应用到自身。

  假设复制集内投票成员的数量为N,则大多数为N/2 + 1,当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。

  之所以使用投票成员数为奇数,是由于偶数节点和奇数节点容忍失效数一致,所以奇数节点从成本和冗余角度讲更具性价比

  容忍失效数=投票成员数-大多数,即容忍失效数=N - N/2 - 1 = N/2 - 1


我的网名不再改
3楼 · 2021-01-22 12:45

学习了如何创建副本集,如何使用副本集,认知了其故障转移策略。我们还需从复制的基础原理了解副本集的工作方式。副本集主要依赖于两个基础机制:oplog和“心跳“。oplog让数据的复制成为可能,而“心跳”则监控健康情况并触发故障转移,后续将看到这些机制时如何运作的。你应该已经逐渐开始理解并能预测副本集的行为了,尤其在故障发生的情况下。

1.关于oplog

    oplog是MongoDB复制的关键。oplog是一个固定集合,位于每个复制节点的local数据库里,记录了所有对数据的变更。每次客户端向主节点写入数据,就会自动向主节点的oplog里添加一个条目,其中包含了足够的信息来再现数据。一旦写操作被复制到某个从节点上,从节点的oplog,从节点的oplog中也会有一条关于写入的数据。每个oplog条目都是由一个BSON时间戳进行标识的,所有的从节点都使用这个时间戳来追中他们最后应用的条目。

   为了更好的理解其原理,我们仔细看看真实的oplog以及其中的记录。连接上主节点后,切换到local数据库。local数据库中保存了所有的副本集元数据和oplog。当然,数据库本身不能被复制。正如其名,local数据库里的数据对本地节点而言是唯一的,因此不该复制。查看local数据库,会发现一个叫oplog.rs的集合,每个副本集都会把oplog保存在这个集合里。如下图所示:

   

    replset.minvalid包含了指定副本集成员的初始同步信息。system.replset保存了副本集配置文档。me和slaves用来实现写关注。startup_log是启动信息。还有replset.election包含了节点的选举信息。我们会将主要的精力集中在oplog上,查询一条前面你插入数据的那个操作相关的op条目。执行:

db.oplog.rs.find({op:"i"})

在主从节点执行的结果相同,如下图:




结果中,第一个字段为ts,保存了该条目BSON时间戳。这里要特别注意,shell使用TimeStamp对象来显示时间戳的,包含两个字段,前面一部分是从纪元开始的秒数,后面一个市计数器。h表示该操作的一个唯一的ID,每个操作的该字段都是一个不同的值。字段op表示操作码,它告诉从节点该条目表示什么操作,本例子中i表示插入。op后的ns标明了有关的命名空间(数据库和集合)。o对插入操作而言包含了所插入的文档的副本。

     在查看oplog条目时,对于那些影响多个文档的操作,oplog会将各个部分都分析到位。对于多项更新和大批量删除来说,会为每个影响到的文档创建单独的oplog条目。例如,你想集合中插入几本狄更斯的数


db.books.insert({title:"A Tale of Two Cities"})db.books.insert({title:"Great Expections"})
db.books.udpate({},{$set:{author:"Dickens"}},false,true)

执行完上述语句后,oplog中查询op:"u"的选项,结果如下:




      如你所见,每个节点都有自己的oplog条目。这种正规化是更通用的一种策略中的一部分。它会保证从节点总是能和主节点拥有一样的数据。要确保这一点,每次应用的操作就必须是幂等的--一个指定的oplog条目被应用多少次都无所谓:结果总是一样的。其他文档操作的行为是一样的,比如删除,你可以试试不同的操作,查看其在oplog中最终是什么样的。要取得oplog的当前状态的基本信息,可以运行


db.getReplicationInfo()



    这里有oplog中第一条河最后一条时间戳,你可以使用$natural排序修饰符手工找到这些oplog条目。例如下面这句用于获取最后一个条目


db.oplog.rs.find().sort({$natural:-1}).limit(1)



      关于复制,还有一件重要的事情。即从节点是如何确定它们在oplog里的位置的。答案在于从节点自己也有一份oplog。这对主从复制的一项重大改进。因此值得深究其中的原理。假设向副本集的主节点发起写操作,接下来会发生什么?写操作先被记录下来,添加到主节点的oplog里。与此同时,所有从节点复制oplog。因此,当某个从节点准备更新自己时,他做了三件事:首先,查看自己oplog中最后一条的时间戳;其次,查询主节点oplog中所有大于此时间戳的条目;最后,把那些条目添加到自己的oplog中并应用到自己的库中。也就是说,万一发生故障,任何被提升为主节点的从节点都会有一个oplog,其他从节点都能以他为复制进行复制。这项特性对副本集的恢复是必须的。


   从节点使用长轮询(long polling)立即应用来自主节点oplog的新条目。因此从节点的数据通常是最新的。由于网络分区或者从节点本身进行维护造成数据陈旧的,可以使用从节点oplog的最新的时间戳来检测网络延迟。

2. 停止复制

      如果从节点在主节点的oplog里找不到它所同步的点,那么就会永久停止复制。发生这种情况是,你会从日志中看到如下异常:


repl:replication data too stale,haltingTus Aug 28 14:19:27[replsecondary]caught SyncException

     回忆一下,oplog是一个固定集合,也就是说集合里的条目最终都会过期。一旦某个从节点没能在主节点的oplog找到它已经同步的点,就无法保证这个从节点是主节点的完美副本了。因为修复停止复制的唯一途径是重新完整同步一次主节点的数据,所以要竭尽全力避免这个状态。为此,要监测从节点的延时情况,针对你的写入要有足够大的oplog。下面我们需要讨论oplog的大小设置。
3. 调整复制OPLOG大小


     因为oplog是一个固定集合,所以一旦创建就无法重新设置大小,为此要慎重选择oplog的大小。默认的oplog大小会随着环境发生变化。在32系统上,oplog默认是50MB,而在64位系统上,oplog会增大到1GB或空余磁盘空间的5%。对于多数部署环境,空闲磁盘空间的5%绰绰有余,对于这种尺寸的oplog,要意识到一旦重写20次,磁盘可能就满了。

    因此默认大小并非适用所有应用程序。如果知道应用程序写入量会很大,在部署之前就应该做些测试。配置好复制,然后以生产环境的写入量向主节点发起写操作,向这样对服务器施压起码一小时,完成后,连接到任意副本集成员上,获取当前复制信息:

db.getReplicationInfo()

    一旦了解了每小时会生成多少oplog,就能决定分配多少oplog空间了。你应该为从节点至少下线八小时做好准备。发生网络故障或类似事件时,要避免任意节点重新同步完整数据,增加oplog的大小能够为你争取更多时间。如果要修改默认的oplog的大小,必须每个成员节点首次启动时使用mongod的-oplogSize选项,其值的单位是兆。可以像下面这样启动一个1GB oplog的mongod实例


mongod -replSet mySet -oplogSize 1024

4. "心跳"检测和故障转移


      副本集的“心跳”检测有助于选举和故障转移。默认情况下,每个副本集成员每两秒钟ping一次其他所有成员。这样一来,系统就可以弄清楚自己的健康状况。在运行rs.status()时,你可以看到每个节点上次“心跳”检测的时间戳和健康状况(1表示健康,0表示没有应答)

     只要每个节点都保持健康且有应答,副本集就能快乐地工作下去。但如果哪个节点失去了相应,副本集就会采取措施。每个副本集都希望确认无论何时都恰好存在一个主节点。但这仅在大多数节点可见时才有可能。例如:如果杀掉从节点,大部分节点依然存在,副本集不会改变状态,只是简单地等待从节点重新上线;如果杀掉主节点,大部分节点依然存在,但没有主节点了。因此从节点自动提升为主节点,如果碰巧有多个从节点,那么会推选状态最新的从节点为主节点。
     但还有其他可能的场景,如果从节点和仲裁节点都被杀掉了,只剩下主节点,但是没有多数节点--原来的三个节点里只有一个节点仍处于健康状态。在这种情况下,主节点的日志中会有如下信息 


Tus Aug 28 19:19:27 [rs Manager] replSet can't see a majority of the set relinquishing primary
Tus Aug 28 19:19:27 [rs Manager] replSet relinquishing primary state
Tus Aug 28 19:19:27 [rs Manager] replSet SECONDARY




   没有了多数节点,主节点会把自己降级为从节点。刚开始有点费解,但仔细想想,如果该节点仍然作为主节点存在的话会发生什么情况呢?如果出于某些网络原因心跳检测失败了,那么其他节点仍然是在线的。如果仲裁节点和从节点依然健在,并能看到对方,那么根据多数节点原则,剩下的从节点会自动变为主节点。要是原来的主节点没有降级,那么就会陷入不堪一击的局面:副本集中有两个主节点。如果应用程序继续运行,就可能对两个不同的主节点做读写操作。肯定会有不一致,并伴随着奇怪的现象。因此,当主节点看不到多数节点时,必须降级为从节点。
5. 提交与回滚
  关于副本集,还有最后一部分需要了解,那就是提交的概念。本质上,你可以一直向主节点做写操作,但是那些写操作在被复制到大多数节点前,都不会被认为是已提交的。这里说的已提交是什么意思呢?举例来说,你向主节点发起一系列写操作,出于某些原因(连接问题、从节点为备份而下线、从节点有延迟等)没有被复制到从节点。现在假设从节点突然被提升为主节点,你向新的主节点写数据,而最终老的主节点再次上线,尝试从新的主节点做复制。这里的问题在于老的主节点里有一系列写操作并未出现在新的主节点的oplog中,这就会触发回滚。

   在回滚时,所有未复制到大多数节点的写操作都会被撤销。也就是说会将它们从从节点的oplog和它们所在的集合里删掉。要是某个从节点登记了一条删除,那么该节点会从其他副本里找到被删除的文档并进行恢复。删除集合以及更新文档的情况也是一样的。

    相关节点数据路径的rollback子目录中保存了被回滚的写操作。针对每个有回滚写操作的集合,会创建一个单独的BSON文件,文件名里包含了回滚的时间。在需要恢复被回滚的文档时,可以用bsondump工具来查看这些BSON文件,并可以通过mongorestore手工进行恢复。

    万一你真的不得不恢复被回滚的数据,你就会意识到应该避免这种情况。幸运的是,从某种程度上来说,这是可以办到的。要是应用程序能够容忍额外的写延时,那么就能用后面会介绍学习的写关注,以此确保每次(也可能是每隔几次)写操作都能被复制到大多数节点上。使用写关注,或者更通用一点,监控复制的延迟,能帮助你减轻甚至避免回滚带来的全部问题。


征戰撩四汸
4楼 · 2022-01-19 16:18

  如果我们的数据库只存在于一台服务器,若这台服务器宕机了,那对于我们的数据将会是灾难,当然这只是其中一个原因,若数据量非常大,读写操作势必会影响数据库的性能,这时候复制就显得相当重要了,因为 MongoDB 可以通过复制,实现读写分离。


  复制是一种在多个服务器上同步数据的过程。


  复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。


  总之,复制可以使你免受硬件故障与服务中断的影响,及时恢复数据。由于数据有多个副本,所以可以将其中一个副本用于灾难恢复、报告或备份。


相关问题推荐

  • 回答 3

    换行。比如,print hello\nworld效果就是helloworld\n就是一个换行符。\是转义的意思,'\n'是换行,'\t'是tab,'\\'是,\ 是在编写程序中句子太长百,人为换行后加上\但print出来是一整行。...

  • 回答 42

    十种常见排序算法一般分为以下几种:(1)非线性时间比较类排序:a. 交换类排序(快速排序、冒泡排序)b. 插入类排序(简单插入排序、希尔排序)c. 选择类排序(简单选择排序、堆排序)d. 归并排序(二路归并排序、多路归并排序)(2)线性时间非比较类排序:...

  • 回答 70
    已采纳

    前景很好,中国正在产业升级,工业机器人和人工智能方面都会是强烈的热点,而且正好是在3~5年以后的时间。难度,肯定高,要求你有创新的思维能力,高数中的微积分、数列等等必须得非常好,软件编程(基础的应用最广泛的语言:C/C++)必须得很好,微电子(数字电...

  • 回答 28

    迭代器与生成器的区别:(1)生成器:生成器本质上就是一个函数,它记住了上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用,跳转到函数上一次挂起的位置。而且记录了程序执行的上下文。生成器不仅记住了它的数据状态,生成器还记住了程序...

  • 回答 9

    python中title( )属于python中字符串函数,返回’标题化‘的字符串,就是单词的开头为大写,其余为小写

  • 回答 6

    第一种解释:代码中的cnt是count的简称,一种电脑计算机内部的数学函数的名字,在Excel办公软件中计算参数列表中的数字项的个数;在数据库( sq| server或者access )中可以用来统计符合条件的数据条数。函数COUNT在计数时,将把数值型的数字计算进去;但是...

  • 回答 1

    head是方法,所以需要取小括号,即dataset.head()显示的则是前5行。data[:, :-1]和data[:, -1]。另外,如果想通过位置取数据,请使用iloc,即dataset.iloc[:, :-1]和dataset.iloc[:, -1],前者表示的是取所有行,但不包括最后一列的数据,结果是个DataFrame。...

  • Python入门简单吗2021-09-23 13:21
    回答 45

    挺简单的,其实课程内容没有我们想象的那么难、像我之前同学,完全零基础,培训了半年,直接出来就工作了,人家还在北京大公司上班,一个月15k,实力老厉害了

  • 回答 4

    Python针对众多的类型,提供了众多的内建函数来处理(内建是相对于导入import来说的,后面学习到包package时,将会介绍),这些内建函数功用在于其往往可对多种类型对象进行类似的操作,即多种类型对象的共有的操作;如果某种操作只对特殊的某一类对象可行,Pyt...

  • 回答 8

     相当于 ... 这里不是注释

  • 回答 4

    还有FIXME

  • 回答 3

    python的两个库:xlrd和xlutils。 xlrd打开excel,但是打开的excel并不能直接写入数据,需要用xlutils主要是复制一份出来,实现后续的写入功能。

  • 回答 8

    单行注释:Python中的单行注释一般是以#开头的,#右边的文字都会被当做解释说明的内容,不会被当做执行的程序。为了保证代码的可读性,一般会在#后面加一两个空格然后在编写解释内容。示例:#  单行注释print(hello world)注释可以放在代码上面也可以放在代...

  • 回答 2

    主要是按行读取,然后就是写出判断逻辑来勘测行是否为注视行,空行,编码行其他的:import linecachefile=open('3_2.txt','r')linecount=len(file.readlines())linecache.getline('3_2.txt',linecount)这样做的过程中发现一个问题,...

  • 回答 4

    或许是里面有没被注释的代码

  • 回答 26

    自学的话要看个人情况,可以先在B站找一下视频看一下

没有解决我的问题,去提问