MySQL主备模式的数据⼀致性解决⽅案
 根据阿⾥交易型业务的特点,以及在双⼗⼀这样业内罕有的需求推动下,我们在官⽅的MySQL基础上增加了⾮常多实⽤的功能、性能补丁。⽽在使⽤MySQL的过程中,数据
⼀致性是绕不开的话题之⼀。本⽂主要从阿⾥巴巴“去IOE”的后时代讲起,向⼤家简单介绍下我们过去⼏年在MySQL数据⼀致性上的努⼒和实践,以及⽬前的解决⽅案。
⼀.MySQL单机的数据⼀致性
  MySQL作为⼀个可插拔的数据库系统,⽀持插件式的存储引擎,在设计上分为Server层和Storage Engine层。
  在Server层,MySQL以events的形式记录数据库各种操作的Binlog⼆进制⽇志,其基本核⼼作⽤有:复制和备份。除此之外,我们结合多样化的业务场景需求,基于Binlog
的特性构建了强⼤的MySQL⽣态,如:DTS、单元化、异构系统之间实时同步等等,Binlog早已成为MySQL⽣态中不可缺少的模块。⽽在Storage Engine层,InnoDB作为⽐较
通⽤的存储引擎,其在⾼可⽤和⾼性能两⽅⾯作了较好的平衡,早已经成为使⽤MySQL的⾸选(PS:官⽅从MySQL 5.5.5开始,将InnoDB作为了MySQL的默认存储引擎)。和
⼤多数关系型数据库⼀样,InnoDB采⽤WAL技术,即InnoDB Redo Log记录了对数据⽂件的物理更改,并保证总是⽇志先⾏,在持久化数据⽂件前,保证之前的redo⽇志已经
写到磁盘。Binlog和InnoDB Redo Log是否落盘将直接影响实例在异常宕机后数据能恢复到什么程度。InnoDB提供了相应的参数来控制事务提交时,写⽇志的⽅式和策略,例
如:
innodb_flush_method:控制innodb数据⽂件、⽇志⽂件的打开和刷写的⽅式,建议取值:fsync、O_DIRECT。
innodb_flush_log_at_trx_commit:控制每次事务提交时,重做⽇志的写盘和落盘策略,可取值:0,1,2。
当innodb_flush_log_at_trx_commit=1时,每次事务提交,⽇志写到InnoDB Log Buffer后,会等待Log Buffer中的⽇志写到Innodb⽇志⽂件并刷新到磁盘上才返回成功。
sync_binlog:控制每次事务提交时,Binlog⽇志多久刷新到磁盘上,可取值:0或者n(N为正整数)。
不同取值会影响MySQL的性能和异常crash后数据能恢复的程度。当sync_binlog=1时,MySQL每次事务提交都会将binlog_cache中的数据强制写⼊磁盘。
innodb_doublewrite:控制是否打开double writer功能,取值ON或者OFF。
当Innodb的page size默认16K,磁盘单次写的page⼤⼩通常为4K或者远⼩于Innodb的page⼤⼩时,发⽣了系统断电/os crash ,刚好只有⼀部分写是成功的,则会遇到partial page write问题,从⽽可能导致crash后由于部分写失败的page影响数据的innodb_support_xa:控制是否开启InnoDB的两阶段事务提交.默认情况下,innodb_support_xa=true,⽀持xa两段式事务提交。
以上参数不同的取值分别影响着MySQL异常crash后数据能恢复的程度和写⼊性能,实际使⽤过程中,需要结合业务的特性和实际需求,来设置合理的配置。⽐如:
MySQL单实例,Binlog关闭场景:
innodb_flush_log_at_trx_commit=1,innodb_doublewrite=ON时,能够保证不论是MySQL Crash 还是OS Crash 或者是主机断电重启都不会丢失数据。
MySQL单实例,Binlog开启场景:
默认innodb_support_xa=ON,开启binlog后事务提交流程会变成两阶段提交,这⾥的两阶段提交并不涉及分布式事务,mysql把它称之为内部xa事务。
当innodb_flush_log_at_trx_commit=1,sync_binlog=1,innodb_doublewrite=ON,innodb_support_xa=ON时,同样能够保证不论是MySQL Crash 还是OS Crash 或者是主机断电重启都不会丢失数据。
但是,当由于主机硬件故障等原因导致主机完全⽆法启动时,则MySQL单实例⾯临着单点故障导致数据丢失的风险,故MySQL单实例通常不适⽤于⽣产环境。
⼆.MySQL集的数据⼀致性
  MySQL集通常指MySQL的主从复制架构。通常使⽤MySQL主从复制来解决MySQL的单点故障问题,其通过逻辑复制的⽅式把主库的变更同步到从库,主备之间⽆法保证
严格⼀致的模式,于是,MySQL的主从复制带来了主从“数据⼀致性”的问题。
  MySQL的复制分为:异步复制、半同步复制、全同步复制。
异步复制
  主库在执⾏完客户端提交的事务后会⽴即将结果返给给客户端,并不关⼼从库是否已经接收并处理,这样就会有⼀个问题,主如果crash掉了,此时主上已经提交的事务可能
并没有传到从库上,如果此时,强⾏将从提升为主,可能导致“数据不⼀致”。早期MySQL仅仅⽀持异步复制。
半同步复制
  MySQL在5.5中引⼊了半同步复制,主库在应答客户端提交的事务前需要保证⾄少⼀个从库接收并写到relay log中,半同步复制通过rpl_semi_sync_master_wait_point参数
来控制master在哪个环节接收 slave ack,master 接收到 ack 后返回状态给客户端,此参数⼀共有两个选项 AFTER_SYNC & AFTER_COMMIT。
配置为WAIT_AFTER_COMMIT
rpl_semi_sync_master_wait_point为WAIT_AFTER_COMMIT时,commitTrx的调⽤在engine层commit之后,如上图所⽰。即在等待Slave ACK时候,虽然没有返回当前客户
端,但事务已经提交,其他客户端会读取到已提交事务。如果Slave端还没有读到该事务的events,同时主库发⽣了crash,然后切换到备库。那么之前读到的事务就不见了,出
现了数据不⼀致的问题,如下图所⽰。图⽚引⾃。
如果主库永远启动不了,那么实际上在主库已经成功提交的事务,在从库上是不到的,也就是数据丢失了。
PS:早在11年前后,阿⾥巴巴数据库就创新实现了在engine层commit之前等待Slave ACK的⽅式来解决此问题。
配置为WAIT_AFTER_SYNC
  MySQL官⽅针对上述问题,在5.7.2引⼊了Loss-less Semi-Synchronous,在调⽤binlog sync之后,engine层commit之前等待Slave ACK。这样只有在确认Slave收到事务
events后,事务才会提交。如下图所⽰,图⽚引⾃:
  在after_sync模式下解决了after_commit模式带来的数据不⼀致的问题,因为主库没有提交事务。但也会有个问题,当主库在binlog flush并且binlog同步到了备库之
后,binlog sync之前发⽣了abort,那么很明显这个事务在主库上是未提交成功的(由于abort之前binlog未sync完成,主库恢复后事务会被回滚掉),但由于从库已经收到了这些
Binlog,并且执⾏成功,相当于在从库上多出了数据,从⽽可能造成“数据不⼀致”。
  此外,MySQL半同步复制架构中,主库在等待备库ack时候,如果超时会退化为异步后,也可能导致“数据不⼀致”。
三.MySQL主备的“数据⼀致性”⽅案
下⾯简单介绍下阿⾥巴巴早期在MySQL数据⼀致性问题的⼀些思考和实践。
1.单元化架构下的“数据⼀致性”
  背景:受机架位限制,单机房或地域总会出现容量瓶颈,业务发展受限;以及跨地域容灾的需求,阿⾥巴巴在早期通过单元化的⽅案来解决。
  由上图看到中⼼和各单元之间通过DTS进⾏实时数据同步,为了保证中⼼和单元的数据⼀致性,我们早期搭建了数据校验和订正平台。主要包括:TCP(terminal compare
platform)全量数据校验订正平台(⽀持表级,库级,实例级,集级别的数据校验)和AMG(Alibaba Magic Glass)实时的增量数据校验订正平台。
  TCP和AMG早已成为阿⾥巴巴数据库⽣态中的核⼼组件,被⼴泛⽤于众多场景中保障数据⼀致性,如:主从复制、单元化同步、逻辑迁移、数据库拆分、字符集升级等。
2.ADHA的回滚和回补
  ADHA(Alibaba Database High Availability)是阿⾥巴巴集团数据库⾼可⽤体系。ADHA的回滚回补功能帮助我们在发⽣切换过程中尽量保证数据质量,将⽼主库还没传到
⽼备库的数据回滚掉rollback,将回滚掉的数据回补到新主库中replay。
ADHA的回滚和回补的⽬的是尽量保证HA切换过程中的数据⼀致性。
3.主从⼀致性保障措施
  复制冲突⾃动处理:MySQL 5.5/5.6/5.7的参数slave_exec_mode⽤于解决主从复制冲突和错误。默认值是STRICT适合于所有模式(不解决冲突),值IDEMPOTENT 会忽
略duplicate-key和no-key-found错误,也不适合解决上⾯主从不⼀致问题。我们从5.6开始给slave_exec_mode增加了⼀个值smart,⽤于⾃动修复⼀些场景(包括PK冲突及UK冲
突引起的HA_ERR_KEY_NOT_FOUND/HA_ERR_FOUND_DUPP_KEY/HA_ERR_END_OF_FILE),具体处理策略如下图
mysql下载哪个盘
  从库复制开启SMART模式,可以修复主从复制中断错误,但不能严格保证主备⼀致,因此当使⽤smart模式修复复制问题后,需要尽快对主从库做⼀个全量数据校验(这⾥
包括TCP全量校验+AMG增量校验),以识别有差异的数据。
4.最⼤保护逻辑 Max Protection
  为了保证主从强⼀致,我们增加了MySQL最⼤保护(maximum protection)模式功能,简称MP模式(这个是参照ORACLE数据库的最⼤保护模式(maximum protection))设计做的,具体由参数 maximum_protection 控制,取值为 ON和OFF )。当配置半同步时,⼀旦判断主从连接断开了,会让主库停⽌对外服务,主库所有当前连接会被KILL,并拒绝接受普通帐号新的连接请求。此刻如果有事务在等待从库回应binlog的同步信息这⼀步,连接是⽆法被kill,该事务在等待超时后会继续⾛完(Engine Commit),然后返回⽹络错误给客户端。即该笔事务被提交了,需要ADHA介⼊回滚掉。MySQL的MP机制是需要ADHA⼀起实现的。
  引⼊最⼤保护逻辑,满⾜了对数据⼀致性要求⾮常⾼的业务场景,如⾦融业务。也给MySQL的⾼可⽤解决⽅案提出更⼤挑战。
  以上都是我们早期在MySQL主备时代关于“数据⼀致性”问题的部分对策,其⽬的都是为了尽可能的保
证“数据⼀致性”,并没有彻底解决“数据⼀致性”问题。然⽽我们相信技术的发展能带来更⼤的运维便利性以及更好的⽤户体验,以Google Spanner以及Amazon Aruora 为代表的NewSQL系统为数据库的“数据⼀致性”给出了与以往不同的思路:基于⼀致性协议!基于⼀致性协议我们构建了⾼性能强⼀致MySQL数据库,RDS三节点企业版。关于⼀致性协议和RDS三节点企业版相关知识下⾯的章节会给⼤家详细介绍,敬请关注!
本⽂作者:jixiang_zy
本⽂为云栖社区原创内容,未经允许不得转载。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。