千家信息网

MongoDB与MySQL关于写确认的异同

发表于:2025-02-12 作者:千家信息网编辑
千家信息网最后更新 2025年02月12日,云妹导读:所谓写确认,是指用户将数据写入数据库之后,数据库告知用户写入成功的一个概念。根据数据库的特点和配置,可以在不同的写入程度上,返回给用户,而这其中,就涉及到了不同的性能、数据安全等级以及数据一
千家信息网最后更新 2025年02月12日MongoDB与MySQL关于写确认的异同

云妹导读:
所谓写确认,是指用户将数据写入数据库之后,数据库告知用户写入成功的一个概念。根据数据库的特点和配置,可以在不同的写入程度上,返回给用户,而这其中,就涉及到了不同的性能、数据安全等级以及数据一致性的内容。

不同的写入确认级别或配置,是数据库提供给用户的一种自我控制的能力,用户可以针对自身业务的特点、数据管理的需要、性能的考虑、数据一致性以及服务可用性各种因素进行考虑,选择适合的数据库配置,来实现自身的需要。

首先介绍几个重要的概念,这些概念也是数据库中常识性的知识了,不过是在不同数据库的不同表述。

这些概念主要涉及到写确认的两个重要考量点, 一个是本地数据库写操作的不丢失,一个是分布式环境下,数据冗余的一致性。

本地数据库写操作是指数据库在处理用户的写操作后,能够持续化,防止因为意外导致的数据丢失,这个主要涉及到日志,比如MySQL中的redo log和MongoDB中的journal日志。

数据冗余的一致性是指多副本的环境下,比如主从或复制集架构下,数据写入主节点后,如何实现从节点与主节点的数据一致,而主从之间是以另外一个日志实现数据同步的,比如MySQL的binlog和MongoDB中的oplog日志。

另外防止主节点崩溃,数据未能同步到从节点,导致从节点成为新的主节点后,未同步数据丢失,也是写确认中重要的内容,即不但同步数据,而且要让数据安全快速的同步。

redo/journal

MySQL的redo log和MongoDB的journal日志都是数据库存储引擎层面的WAL(Write-Ahead Logging)预写式日志,记录的是数据的物理修改,是提高数据系统持久性的一种技术。

redo log

redo log是MySQL的默认存储引擎innodb事务日志中的核心日志文件之一,俗称重做日志,主要用作前滚的数据恢复。

当我们想要修改MySQL数据库中某一行数据的时候,innodb是把数据从磁盘读取到内存的缓冲池上进行修改。这个时候数据在内存中被修改,与磁盘中相比就存在了差异,我们称这种有差异的数据为脏页。innodb对脏页的处理不是每次生成脏页就将脏页刷新回磁盘,这样会产生海量的io操作,严重影响innodb的处理性能,因此并不是每次有了脏页都立刻刷新到磁盘中。既然脏页与磁盘中的数据存在差异,那么如果在这期间数据库出现故障就会造成数据的丢失。

而redo log就是为了解决这个问题。由于redo log的存在,可以延迟刷新脏页到磁盘的时间,保障了数据库性能的情况下提高了数据的安全。虽然增加了redo log刷新的开销,但是由于redo log采用的顺序io,比数据页的随机io要快很多,这额外的开销可接受。

即,数据库先将数据页的物理修改情况写到刷盘较快的redo log文件中,防止数据丢失。一旦发生故障,数据库重启恢复的时候,可以先从redo log把未刷新到磁盘的已经提交的物理数据页恢复回来。

journal

journal是MongoDB存储引擎层面的概念,MongoDB主要支持的mmapv1、wiredtiger、mongorocks等存储引擎,都⽀持配置journal。MongoDB可以基于journal来恢复因为崩溃未及时写到磁盘的信息。

MongoDB 所有的数据写⼊、读取最终都是调存储引擎层的接⼝来存储、读取数据,journal 是存储引擎存储数据时的一种辅助机制。

在MongoDB的4.0版本以前,用户可以设置是否开启journal日志;从4.0版本开始,副本集成员必须开启journal功能。

以wiredtiger为例,如果不配置journal,写入wiredtiger的数据,并不会立即持久化存储;而是每分钟会做一次全量的checkpoint( storage.syncPeriodSecs配置项,默认为1分钟),将所有的数据持久化。如果中间出现宕机,那么数据只能恢复到最近的一次checkpoint,这样最多可能丢掉1分钟的数据。

所以建议「一定要开启journal」,开启journal后,每次写入会记录一条操作日志(通过journal可以重新构造出写入的数据)。这样即使出现宕机,启动时 Wiredtiger 会先将数据恢复到最近的一次checkpoint的点,然后重放后续的 journal操作日志来恢复数据。

binlog/oplog

MySQL的binlog和MongoDB的oplog都是数据库层面的写操作对应的逻辑日志,主要用于实现数据在主备之间的同步复制以及增量备份和恢复。

binlog

binlog是MySQL数据库层面的一种二进制日志,不管底层使用的什么存储引擎,对数据库的修改都会产生这种日志。binlog记录操作的方法是逻辑性语句,可以通过设置log-bin=mysql-bin来启动该功能。

binlog中记录了有关写操作的执行时间、操作类型、以及操作的具体内容,比如SQL语句(statement)或每行实际数据的变更(row)。

上图是MySQL主从之间是如何实现数据复制的,其中的三个重要过程是:

  • 主库(Master)把数据库更改记录到binlog(图中的Binary Log)中;
  • 备库(Slave)将主库上的binlog复制到自己的中继日志(Relay log)中;
  • 备库读取中继日志中的事件,将其重放(Replay)到备库数据之上。

这样源源不断的复制,实现了数据在数据库节点之间的一致。

oplog

oplog是MongoDB数据库层面的概念,在复制集架构下,主备节点之间通过oplog来实现节点间的数据同步。Primary中所有的写入操作都会记录到MongoDB Oplog中,然后从库会来主库一直拉取Oplog并应用到自己的数据库中。这里的Oplog是MongoDB local数据库的一个集合,它是Capped collection,通俗意思就是它是固定大小,循环使用的。

oplog 在 MongoDB 里是一个普通的 capped collection,对于存储引擎来说,oplog只是一部分普通的数据而已。

只有按复制集架构启动的节点会自动在local库中创建oplog.rs的集合。

oplog中记录了有关写操作的操作时间、操作类型、以及操作的具体内容,几乎保留的每行实际数据的变更(在4.0及以后版本中,一个事务中涉及的多个文档,会写在一条oplog中)。

上图是MongoDB主备之间如何实现数据复制的,其中的四个重要过程是:

  • 主库(Primary)把数据库更改记录到oplog(图中的Capped Oplog集合)中;
  • 备库(Secondary)把主库上的oplog拉取到自己的回放队列中(Queue)中;
  • 备库读取队列中的oplog,批量回放(applyOps)到备库数据中;
  • 再将队列中的Oplog写入到备库中的oplog.rs集合中。

这样源源不断的复制,实现了数据在数据库节点之间的一致。

另外MongoDB支持链式复制,即oplog不一定从Primary中获取,还可以从其他Secondary获取。上图是MongoDB主备之间如何实现数据复制的,其中的四个重要过程是:

  • 主库(Primary)把数据库更改记录到oplog(图中的Capped Oplog集合)中;
  • 备库(Secondary)把主库上的oplog拉取到自己的回放队列中(Queue)中;
  • 备库读取队列中的oplog,批量回放(applyOps)到备库数据中;
  • 再将队列中的Oplog写入到备库中的oplog.rs集合中。

这样源源不断的复制,实现了数据在数据库节点之间的一致。

另外MongoDB支持链式复制,即oplog不一定从Primary中获取,还可以从其他Secondary获取。

redo与binlog

  1. redo log是在innodb存储引擎层产生,而binlog是MySQL数据库的上层产生的,并且binlog不仅仅针对innodb存储引擎,MySQL数据库中的任何存储引擎对于数据库的更改都会产生binlog。
  2. 两种日志记录的内容形式不同。MySQL的binlog是逻辑日志,其记录是对应的SQL语句或行的修改内容。而innodb存储引擎层面的redo log是物理日志。
  3. 两种日志与记录写入磁盘的时间点不同,binlog只在事务提交完成后进行一次写入。而innodb存储引擎的redo log在事务进行中不断地被写入,并日志不是随事务提交的顺序进行写入的。
  4. binlog仅在事务提交时记录,并且对于每一个事务,仅在事务提交时记录,并且对于每一个事务,仅包含对应事务的一个日志。而对于innodb存储引擎的redo log,由于其记录是物理操作日志,因此每个事务对应多个日志条目,并且事务的redo log写入是并发的,并非在事务提交时写入,其在文件中记录的顺序并非是事务开始的顺序。
  5. binlog不是循环使用,在写满或者重启之后,会生成新的binlog文件,redo log是循环使用。
  6. binlog可以作为恢复数据使用,主从复制搭建,redo log作为异常宕机或者介质故障后的数据恢复使用。

journal与oplog

journal日志是在wiretiger、mmapV1等存储引擎层产生,而oplog是MongoDB数据库的主从复制层面的概念,oplog也与存储引擎无关;

两种日志记录的内容形式不同。MongoDB的oplog是逻辑日志,其记录的是对应的写操作的内容。而journal存储的物理修改;

两种日志与记录写入磁盘的时间点不同。

MongoDB 复制集里写入一个文档时,需要修改如下数据

  1. 将文档数据写入对应的集合
  2. 更新集合的所有索引信息
  3. 写入一条oplog用于同步 最终存储引擎会将所有修改操作应用,并将上述3个操作写⼊到一条 journal 操作日志里。
  4. journal不是循环使用,在写满或者重启之后,会生成新的journal文件,oplog是循环使用;
  5. oplog可以作为恢复数据使用,复制集架构,journal作为一场宕机或者介质故障后的数据恢复使用。

写确认

写确认这个概念其实是来自于MongoDB中的write concern,描述的是MongoDB对一个写操作的确认(acknowledge)等级。而MySQL中对应的这个概念,可以理解为,用户在提交(commit)写操作的时候,需要经过哪些操作之后就会告知用户提交成功。

MongoDB

在MongoDB中,数据库支持基于write concern功能使用户配置灵活的写入策略,则不同的策略对应不同的数据写入程度即返回给用户写入成功,用户可以继续操作下一个写请求。

write concern

write concern支持3个配置项:

{ w: , j: , wtimeout: }

其中:

  1. w,该参数要求写操作已经写入到个节点才向用户确认;
    1. {w: 0} 对客户端的写入不需要发送任何确认,适用于性能要求高,但不关注正确性的场景;
    2. {w: 1} 默认的writeConcern,数据写入到Primary就向客户端发送确认;
    3. {w: "majority"} 数据写入到副本集大多数成员后向客户端发送确认,适用于对数据安全性要求比较高的场景,该选项会降低写入性能;
  2. j,该参数表示是否写操作要进行journal持久化之后才向用户确认;
    1. {j: true} 要求primary写操作进行了journal持久化之后才向用户确认;
    2. {j: false} 要求写操作已经在journal缓存中即可向用户确认;journal后续会将持久化到磁盘,默认是100ms;
  3. wtimeout,该参数表示写入超时时间,w大于1时有效;当w大于1时,写操作需要成功写入若干个节点才算成功,如果写入过程中节点有故障,导致写操作迟迟不能满足w要求,也就一直不能向用户返回确认结果,为了防止这种情况,用户可以设置wtimeout来指定超时时间,写入过程持续超过该时间仍未结束,则认为写入失败。

副本集下的写确认

下面以一个副本集架构来描述,一个写操作的流程,来认识MongoDB下的写确认。

上面这个写操作,{w:2},需要至少两个节点写成功才可以返回给用户写成功;而每个节点的写入成功可以基于参数{j}来判断,如果{j:true},则每个节点写入操作的journal都刷盘才可以;如果{j:false},则写入操作的journal在缓存中即可以返回成功;

另外,MongoDB的Primary如何知道Secondary是否已经同步成功呢,是基于如下流程:

  1. Client向Primary发起请求,指定writeConcern为{w: "majority"},Primary收到请求,本地写入并记录写请求到oplog,然后等待大多数节点都同步了这条/批oplog(Secondary应用完oplog会向主报告最新进度);
  2. Secondary拉取到Primary上新写入的oplog,本地重放并记录oplog。为了让Secondary能在第一时间内拉取到主上的oplog,find命令支持一个awaitData的选项,当find没有任何符合条件的文档时,并不立即返回,而是等待最多maxTimeMS(默认为2s)时间看是否有新的符合条件的数据,如果有就返回;所以当新写入oplog时,备立马能获取到新的oplog;
  3. Secondary上有单独的线程,当oplog的最新时间戳发生更新时,就会向Primary发送replSetUpdatePosition命令更新自己的oplog时间戳;
  4. 当Primary发现有足够多的节点oplog时间戳已经满足条件了,向客户端发送确认,这样,Primary即可知道数据已经同步到了。

MySQL

MySQL数据库在所谓写确认或写成功方面可以通过执行事务的commit提交来体现,提交成功则为写成功。因此我主要从事务在执行事务以及commit事务的过程中,涉及的redo log、binlog以及两种日志的刷盘和主从复制的流程来分析MySQL的写成功相关的设置和问题。

MySQL复制架构

目前MySQL较为流量的版本包括5.5、5.6、5.7、8.0,而8.0版本中使用的Group Replication来实现多节点的数据一致性,这种组复制依靠分布式一致性协议(Paxos协议的变体),实现了分布式下数据的最终一致性。

MySQL中有几种常见复制机制:

  • 同步复制。当主库提交事务之后,所有的从库节点必须收到、Replay并且提交这些事务,然后主库线程才能继续做后续操作。但缺点是,主库完成一个事务的时间会被拉长,性能降低。
  • 异步复制。主库将事务 Binlog 事件写入到 Binlog文件中,此时主库只会通知一下 Dump 线程发送这些新的Binlog,然后主库就会继续处理提交操作,而此时不会保证这些 Binlog 传到任何一个从库节点上。

  • 半同步复制。是介于全同步复制与全异步复制之间的一种,主库只需要等待至少一个从库节点收到并且 Flush Binlog 到 Relay Log 文件即可,主库不需要等待所有从库给主库反馈。同时,这里只是一个收到的反馈,而不是已经完全完成并且提交的反馈,如此,节省了很多时间。

  • 组复制。由若干个节点共同组成一个复制组,一个事务的提交,必须经过组内大多数节点(N / 2 + 1)决议并通过,才能得以提交。比如由3个节点组成一个复制组,Consensus层为一致性协议层,在事务提交过程中,发生组间通讯,由2个节点决议(certify)通过这个事务,事务才能够最终得以提交并响应。

除了组复制,半同步复制技术是性能和安全相对更好的设计,尤其在5.7版本中,优化了之前版本的半同步复制相关的逻辑,因此我们主要以5.7版本来介绍。

MySQL5.6/5.5半同步复制的原理:提交事务的线程会被锁定,直到至少一个Slave收到这个事务,由于事务在被提交到存储引擎之后才被发送到Slave上,所以事务的丢失数量可以下降到最多每线程一个。因为事务是在被提交之后才发送给Slave的,当Slave没有接收成功,并且Master挂了,会导致主从不一致:主有数据,从没有数据。这个被称为AFTER_COMMIT。


MySQL5.7在Master事务提交的时间方面做了改进,事务是在提交之前发送给Slave(AFTER_SYNC),当Slave没有接收成功,并且Master宕机了,不会导致主从不一致,因为此时主还没有提交,所以主从都没有数据。


不过假如Slave接收成功,并且Master中的binlog未来得及刷盘并且在存储引擎提交之前宕机了,那么很明显这个事务是不成功的,但由于对应的Binlog已经做了Sync操作,从库已经收到了这些Binlog,并且执行成功,相当于在从库上多了数据,也算是有问题的,但多了数据,问题一般不算严重。此时可能就需要8.0版本中的组复制了。

MySQL写确认行为

我们以MySQL的5.7版本的半同步复制的主从架构的为例子,来介绍MySQL各个参数对写确认即commit的不同影响。

上图中,能够体现半同步复制(AFTER SYNC)的过程为:

  • 第6步,写binlog;(sync_binlog参数在此起作用)
  • 第7步,同步binlog到备库的Relay log;(sync_relay_log参数在此起作用)
  • 第8步,返回给主库ack;
  • 第9步和第10步,提交事务,将redo log中该事务标记为已提交;(innodb_flush_log_at_trx_commit参数在此起作用)
  • 第11步,返回给用户写成功;

上图中,能体现redo log和binlog顺序一致性的过程为:

  • 第4步,将redo log置为prepare并刷盘;
  • 第6步,写入binlog;(sync_binlog参数在此起作用)
  • 第9步和第10步,提交事务,并将redo log置为提交状态;

下面将把上面介绍的与刷盘有关的配置项引入这整个过程,来看写操作不同的行为和风险。

注意:以上是在开始内部两阶段提交的流程,即innodb_support_xa=true,这个时候可以通过判断binlog来恢复会提交的事务,因此innodb_flush_log_at_trx_commit看起来可有可无;如果未开启内部事务的两阶段提交,则更会复杂,只有innodb_flush_log_at_trx_commit = 1 且 sync_binlog = 1 且 sync_relay_log = 1的情况下,可以保证已提交事务的安全,其他情况都有可能导致数据丢失或者主从数据不一致的风险。

但是在innodb_flush_log_at_trx_commit = 1 且 sync_binlog = 1 且 sync_relay_log = 1 的情况下,MySQL的性能相对最低。可以在提高性能的情况下,比如 innodb_flush_log_at_trx_commit = 2 且 sync_binlog = N (N为500 或1000),由于这种情况,redo log和binlog都在系统缓存中,可以使用带蓄电池后备电源的缓存cache,防止系统断电异常。

此外,rpl_semi_sync_master_wait_for_slave_count参数是控制同步到多少个节点的,类似MongoDB中write concern中的 w 参数,如果这个参数设置为0(其实不能,最低1),则变为了纯粹的异步复制;如果这个参数设置为最大(所有从节点个数),则变为了纯粹的同步复制,因此这个地方也可以根据需要来进行调整,来提交数据的安全。

同时,有可能影响同步模式的还包括rpl_semi_sync_master_wait_no_slave参数、影响复制等待超时的参数rpl_semi_sync_master_timeout等。当rpl_semi_sync_master_wait_no_slave为OFF时,只要master发现Rpl_semi_sync_master_clients小于rpl_semi_sync_master_wait_for_slave_count,则master立即转为异步模式;如果为ON时,如果在事务提交阶段(master等待ACK)超时rpl_semi_sync_master_timeout,master会转为异步模式。

对比

配置比较

其他

虽然MongoDB和MySQL在很多方面可以有类似或相似的设置,但是还是存在一些区别,比如:

  1. MongoDB的journal中包含了oplog的信息;而binlog和redo log是两个相对独立的内容;
  2. MySQL几乎所有的写操作都是基于事务来提交的;而MongoDB在4.0开始支持多文档的事务,单文档的事务基于内部事务逻辑实现,未直接提供给用户;
  3. MySQL的写确认是已commit提交成功为标志,MongoDB的普通写操作是返回写入成功为标志;事务写操作也是已commit为标志;

总结

本文章所介绍的写确认的概念,涉及到了MongoDB与MySQL的日志文件(redo log/journal)、同步用日志(binlog/oplog)、刷盘机制和时机、主从同步架构等多个流程和模块,目的就是实现写操作的原子性、持久性、分布式环境下的数据一致性等,对数据的性能和安全都有影响,需要根据数据、业务、压力、安全等客观因素去调整。

由于涉及的内容非常多,未对所有的情况进行测试验证,可能有疏漏或错误,希望大家不吝赐教。也希望本篇内容对于对MySQL和MongoDB都有兴趣的同学可以作为一个总结和参考。


参考资料

高性能MySQL( https://item.jd.com/11220393.ht ml
MongoDB官方手册( https://docs.mongodb.com/manual /
深入浅出MongoDB复制( https://mongoing.com/archives/5 200
mysql基于binlog的复制( https://blog.csdn.net/u01254801 6/article/details/86584293
MongoDB journal 与 oplog,究竟谁先写入?( https://mongoing.com/archives/3 988
MySQL5.7新特性--官方高可用方案MGR介绍( https://www.cnblogs.com/luoahong/ar ticles/8043035.html
MongoDB writeConcern原理解析( https://mongoing.com/archives/2 916
mysql日志系统之redo log和bin log( https://www.jianshu.com/p/4bcfffb27 ed5
MySQL 5.7 半同步复制增强【转】( https://www.cnblogs.com/mao3714/p/8 777470.html
MySQL 中Redo与Binlog顺序一致性问题 【转】( https://www.cnblogs.com/mao3714/p/8 734838.html
详细分析MySQL事务日志(redo log和undo log)( https://www.cnblogs.com/f-ck-need-u /archive/2018/05/08/9010872.html
MySQL 的"双1设置"-数据安全的关键参数(案例分享)( https://www.cnblogs.com/kevingrace/ p/10441086.html
rpl_semi_sync_master_wait_no_slave 参数研究实验( https://www.cnblogs.com/konggg/p/12 205505.html
MySQL5.7新特性半同步复制之AFTER_SYNC/AFTER_COMMIT的过程分析和总结( http://blog.itpub.net/15498/vi ewspace-2143986/

以上,Enjoy~

点击【 阅读】,可了解更多数据库相关详请

数据 事务 数据库 日志 节点 同步 存储 用户 引擎 一致 参数 成功 时间 不同 一致性 内容 性能 磁盘 过程 主从 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 盐城java软件开发管理 个人搭建云服务器配置 无法访问连接管理器服务器 企业微信服务器异常请稍后重试 软件开发的基础教程教学视频 天津互联网线上科技公司 软件开发实验室网站 东宁市2019年网络安全宣传周 软件开发厂 绝地求生未来之役服务器推荐 校园网络安全目标管理责任书 memcache服务器 计算机网络技术又和创新 服务器中的资源主要是网络资源吗 广播电视网络安全考试题 四大基础数据库 数据库视图中文乱码 服务器咋发全服播报 通过网络技术窥探他人隐私 首选dns服务器在哪里查询 河东区企业网络技术不二之选 大气科学专业做软件开发 gmod正版服务器 如何将电脑设置成串口服务器 互联网公司能称得高科技吗 cas部署服务器最低配置 古冶区辅助网络技术售后保障 相同字段数据库怎么合并 芜湖点餐系统软件开发哪家好 金山词霸软件开发
0