云妹导读:
所谓写确认,是指用户将数据写入数据库以后,数据库告知用户写入成功的一个概念。根据数据库的特色和配置,能够在不一样的写入程度上,返回给用户,而这其中,就涉及到了不一样的性能、数据安全等级以及数据一致性的内容。
不一样的写入确认级别或配置,是数据库提供给用户的一种自我控制的能力,用户能够针对自身业务的特色、数据管理的须要、性能的考虑、数据一致性以及服务可用性各类因素进行考虑,选择适合的数据库配置,来实现自身的须要。html
首先介绍几个重要的概念,这些概念也是数据库中常识性的知识了,不过是在不一样数据库的不一样表述。mysql
这些概念主要涉及到写确认的两个重要考量点,一个是本地数据库写操做的不丢失,一个是分布式环境下,数据冗余的一致性。sql
本地数据库写操做是指数据库在处理用户的写操做后,可以持续化,防止由于意外致使的数据丢失,这个主要涉及到日志,好比MySQL中的redo log和MongoDB中的journal日志。mongodb
数据冗余的一致性是指多副本的环境下,好比主从或复制集架构下,数据写入主节点后,如何实现从节点与主节点的数据一致,而主从之间是以另一个日志实现数据同步的,好比MySQL的binlog和MongoDB中的oplog日志。数据库
另外防止主节点崩溃,数据未能同步到从节点,致使从节点成为新的主节点后,未同步数据丢失,也是写确认中重要的内容,即不但同步数据,并且要让数据安全快速的同步。缓存
MySQL的redo log和MongoDB的journal日志都是数据库存储引擎层面的WAL(Write-Ahead Logging)预写式日志,记录的是数据的物理修改,是提升数据系统持久性的一种技术。安全
redo log是MySQL的默认存储引擎innodb事务日志中的核心日志文件之一,俗称重作日志,主要用做前滚的数据恢复。架构
当咱们想要修改MySQL数据库中某一行数据的时候,innodb是把数据从磁盘读取到内存的缓冲池上进行修改。这个时候数据在内存中被修改,与磁盘中相比就存在了差别,咱们称这种有差别的数据为脏页。innodb对脏页的处理不是每次生成脏页就将脏页刷新回磁盘,这样会产生海量的io操做,严重影响innodb的处理性能,所以并非每次有了脏页都马上刷新到磁盘中。既然脏页与磁盘中的数据存在差别,那么若是在这期间数据库出现故障就会形成数据的丢失。并发
而redo log就是为了解决这个问题。因为redo log的存在,能够延迟刷新脏页到磁盘的时间,保障了数据库性能的状况下提升了数据的安全。虽然增长了redo log刷新的开销,可是因为redo log采用的顺序io,比数据页的随机io要快不少,这额外的开销可接受。app
即,数据库先将数据页的物理修改状况写到刷盘较快的redo log文件中,防止数据丢失。一旦发生故障,数据库重启恢复的时候,能够先从redo log把未刷新到磁盘的已经提交的物理数据页恢复回来。
journal是MongoDB存储引擎层面的概念,MongoDB主要支持的mmapv一、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操做日志来恢复数据。
MySQL的binlog和MongoDB的oplog都是数据库层面的写操做对应的逻辑日志,主要用于实现数据在主备之间的同步复制以及增量备份和恢复。
binlog是MySQL数据库层面的一种二进制日志,无论底层使用的什么存储引擎,对数据库的修改都会产生这种日志。binlog记录操做的方法是逻辑性语句,能够经过设置log-bin=mysql-bin来启动该功能。
binlog中记录了有关写操做的执行时间、操做类型、以及操做的具体内容,好比SQL语句(statement)或每行实际数据的变动(row)。
上图是MySQL主从之间是如何实现数据复制的,其中的三个重要过程是:
这样源源不断的复制,实现了数据在数据库节点之间的一致。
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主备之间如何实现数据复制的,其中的四个重要过程是:
这样源源不断的复制,实现了数据在数据库节点之间的一致。
另外MongoDB支持链式复制,即oplog不必定从Primary中获取,还能够从其余Secondary获取。上图是MongoDB主备之间如何实现数据复制的,其中的四个重要过程是:
这样源源不断的复制,实现了数据在数据库节点之间的一致。
另外MongoDB支持链式复制,即oplog不必定从Primary中获取,还能够从其余Secondary获取。
journal日志是在wiretiger、mmapV1等存储引擎层产生,而oplog是MongoDB数据库的主从复制层面的概念,oplog也与存储引擎无关;
两种日志记录的内容形式不一样。MongoDB的oplog是逻辑日志,其记录的是对应的写操做的内容。而journal存储的物理修改;
两种日志与记录写入磁盘的时间点不一样。
MongoDB 复制集里写入一个文档时,须要修改以下数据
写确认这个概念实际上是来自于MongoDB中的write concern,描述的是MongoDB对一个写操做的确认(acknowledge)等级。而MySQL中对应的这个概念,能够理解为,用户在提交(commit)写操做的时候,须要通过哪些操做以后就会告知用户提交成功。
在MongoDB中,数据库支持基于write concern功能使用户配置灵活的写入策略,则不一样的策略对应不一样的数据写入程度即返回给用户写入成功,用户能够继续操做下一个写请求。
write concern
write concern支持3个配置项:
{ w: , j: , wtimeout: }
其中:
副本集下的写确认
下面以一个副本集架构来描述,一个写操做的流程,来认识MongoDB下的写确认。
上面这个写操做,{w:2},须要至少两个节点写成功才能够返回给用户写成功;而每一个节点的写入成功能够基于参数{j}来判断,若是{j:true},则每一个节点写入操做的journal都刷盘才能够;若是{j:false},则写入操做的journal在缓存中便可以返回成功;
另外,MongoDB的Primary如何知道Secondary是否已经同步成功呢,是基于以下流程:
MySQL数据库在所谓写确认或写成功方面能够经过执行事务的commit提交来体现,提交成功则为写成功。所以我主要从事务在执行事务以及commit事务的过程当中,涉及的redo log、binlog以及两种日志的刷盘和主从复制的流程来分析MySQL的写成功相关的设置和问题。
MySQL复制架构
目前MySQL较为流量的版本包括5.五、5.六、5.七、8.0,而8.0版本中使用的Group Replication来实现多节点的数据一致性,这种组复制依靠分布式一致性协议(Paxos协议的变体),实现了分布式下数据的最终一致性。
MySQL中有几种常见复制机制:
除了组复制,半同步复制技术是性能和安全相对更好的设计,尤为在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)的过程为:
上图中,能体现redo log和binlog顺序一致性的过程为:
下面将把上面介绍的与刷盘有关的配置项引入这整个过程,来看写操做不一样的行为和风险。
注意:以上是在开始内部两阶段提交的流程,即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在不少方面能够有相似或类似的设置,可是仍是存在一些区别,好比:
本文章所介绍的写确认的概念,涉及到了MongoDB与MySQL的日志文件(redo log/journal)、同步用日志(binlog/oplog)、刷盘机制和时机、主从同步架构等多个流程和模块,目的就是实现写操做的原子性、持久性、分布式环境下的数据一致性等,对数据的性能和安全都有影响,须要根据数据、业务、压力、安全等客观因素去调整。
因为涉及的内容很是多,未对全部的状况进行测试验证,可能有疏漏或错误,但愿你们不吝赐教。也但愿本篇内容对于对MySQL和MongoDB都有兴趣的同窗能够做为一个总结和参考。
参考资料
高性能MySQL(https://item.jd.com/11220393.html)
MongoDB官方手册(https://docs.mongodb.com/manual/)
深刻浅出MongoDB复制(https://mongoing.com/archives/5200)
mysql基于binlog的复制(https://blog.csdn.net/u012548016/article/details/86584293)
MongoDB journal 与 oplog,究竟谁先写入?(https://mongoing.com/archives/3988)
MySQL5.7新特性--官方高可用方案MGR介绍(https://www.cnblogs.com/luoahong/articles/8043035.html)
MongoDB writeConcern原理解析(https://mongoing.com/archives/2916)
mysql日志系统之redo log和bin log(https://www.jianshu.com/p/4bcfffb27ed5)
MySQL 5.7 半同步复制加强【转】(https://www.cnblogs.com/mao3714/p/8777470.html)
MySQL 中Redo与Binlog顺序一致性问题 【转】(https://www.cnblogs.com/mao3714/p/8734838.html)
详细分析MySQL事务日志(redo log和undo log)(https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html)
MySQL 的"双1设置"-数据安全的关键参数(案例分享)(http://www.javashuo.com/article/p-dlnoxkdg-do.html)
rpl_semi_sync_master_wait_no_slave 参数研究实验(https://www.cnblogs.com/konggg/p/12205505.html)
MySQL5.7新特性半同步复制之AFTER_SYNC/AFTER_COMMIT的过程分析和总结(http://blog.itpub.net/15498/viewspace-2143986/)
以上,Enjoy~
点击【阅读】,可了解更多数据库相关详请