MySQL是一个RDBMS(关系型数据库管理系统),由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。由于其体积小、速度快、拥有成本低,尤其是开放源码这一特点,广受各大企业欢迎,包括腾讯,阿里,百度,网易,Google,FaceBook等互联网巨头企业。

随着互联网的高速发展,互联网服务可用性变得越发重要,数据容灾也随之成为各企业的关键任务。在数据容灾中,数据库集群如何处理数据一致性也成为了各企业需要解决的问题。特别在一些新兴的金融服务中,MySQL也逐渐成为其核心数据库,如何保证金钱的准确性则尤为重要。MySQL也从一开始的异步复制,到Google开发的半同步复制,到MySQL 5.7更新的lossless半同步复制,一直在优化集群的数据一致性问题。

虽然MySQL一直在优化数据的一致性问题,但问题依然存在,使得各大企业纷纷各自设计一套MySQL补丁来保证数据一致。腾讯数平的TDSQL,腾讯微信的PhxSQL,阿里的AliSQL,网易的InnoSQL等设计都是为了保证数据一致性。MySQL5.7发布的lossless半同步,虽然宣称zero loss,解决了5.6版本中有可能出现的data lost问题,但其数据一致性仍未完全解决。

MySQL半同步复制的问题

图1 MySql半同步流程

图1描述了MySQL的Binlog半同步过程。Wait ACK是半同步的关键步骤,Master把Binlog发给Slave之后,需要等待Slave的ACK。Master直到成功收到ACK之后,才执行Engine Commit把数据持久化到Storage。具体细节可参考:http://my-replication-life.blogspot.com/2013/09/loss-less-semi-synchronous-replication.html

MySQL启动时,Wait ACK过程会被跳过,导致Engine Commit会被直接执行。具体细节请参考:https://jira.mariadb.org/browse/MDEV-162

下面对MySQL的数据在Master和Slave之间是否能保证一致进行简单分析。讨论均基于各机器数据最终是否一致来展开。下面的分析只针对半同步复制,且假设半同步失败后不会退化成异步复制。

场景1:Master正常工作

Master的数据复制到Slave,Slave与Master保持数据一致。

场景2:Master Crash且不切换Master

场景2.1

Master已经收到ACK,并执行Engine Commit。Slave与Master保持数据一致。

场景2.2

Master处于Wait ACK阶段,存在PendingBinlog(未执行Engine Commit的Binlog)。

图2 Master重启时执行EngineCommit,并把Binlog重新复制给Slave

Master重启时执行EngineCommit。Slave重新连接Master,Binlog重新开始复制,随后Slave数据和Master一致。如图2。

因此,在MySql5.7的情况下,场景2.2能保证Master和Slave之间的数据一致性。但是在MySQL5.6及之前的版本,场景2.2是不能保证数据一致性的,具体请参考:http://my-replication-life.blogspot.com/2013/09/loss-less-semi-synchronous-replication.html

场景3:Master Crash且切换Master

场景3.1

旧Master Crash时,已经收到至少一台Slave的ACK并执行Engine Commit。

数据已复制到至少一台Slave,该Slave与旧Master的数据保持一致。

场景3.2

旧Master处于Wait ACK阶段时Crash,新Master被切换到了一台拥有最新Binlog的Slave。

场景3.2中,旧Master中的PendingBinlog存在两种场景。

场景3.2.1

旧Master Crash时Binlog发送失败,未复制给任何Slave。

图3 机器A重启Commit Transaction X。机器A/B数据不一致。

图4 机器B接收到事务X的重试请求(事务X’)且复制到机器A。

机器A/B数据可能不一致。

假设机器A为旧Master,执行事务X时,复制失败并Crash。随后机器B成为新Master。机器A重启时执行Engine Commit,事务X被Commit。此时机器A和机器B的数据一致性被破坏。两台机器上数据可能不一致。如图3,图4。

数据不一致的原因是机器A在重启时对PendingBinlog执行Engine Commit。在切换了Master的情况下,只能通过回滚PendingBinlog解决。

场景3.2.2

旧Master Crash时Binlog发送成功,但还未执行Engine Commit。

图6 机器A重启马上执行Engine Commit,数据一致

假设机器A为旧Master,执行事务X时在执行Commit前Crash,但机器B收到事务X。随后机器B成为新Master。

机器A重启时对PendingBinlog执行Engine Commit,执行成功后机器A的数据是机器B的子集。此时机器A可从机器B中拉取最新的数据。另外一台Slave机器C可以从这两台机器中任意拉取。

从图6可以看出,机器A在出现故障时,由于TransactionX已经复制给其中一台Slave和重启时立刻Commit Transaction X,使得该Slave和Master的数据能保证一致。

图7 两台机器出现故障,Master切换可能会丢失数据

上述讨论都是基于拥有最新数据的Slave和Master不能一起出现故障。当这两台机器一起出现故障时,进行Master切换则会造成数据丢失。如图7。

对于较小的集群(机器数目小于或者等于3),当出现两台机器一起发生故障时,可认为集群已无法提供服务(半同步复制无法工作)。

对于较大的集群(机器数目大于3),当出现两台机器一起发生故障,且无法得知该两台机器的数据状态时,该集群也无法提供服务(无法确认拥有最新数据的Slave是否包含在故障机器中)。因此,对于较大的集群,通常增加半同步复制等待ACK的数目,使得出现上述状况时,仍能进行Master切换(非故障机器中,存在拥有最新数据的机器)。

增加等待ACK的数目,解决了数据丢失的问题,但同时给数据回滚带来了难题。

图8

如图8。假设MySQL集群有5台机器,半同步复制需要等待2台Slave的ACK。机器A为旧Master,在执行Wait ACK阶段,机器B收到Binlog后,机器A和机器B同时Crash或者被隔离,导致Binlog复制失败。根据场景3.2.1的分析,当机器C成为Master后,机器A和机器B在恢复服务前需要对其进行数据回滚。但对Slave进行数据回滚较为困难。且若回滚失败,则会出现数据不一致。

对于较小的集群,回滚PendingBinlog比较容易实现。但对于较大的集群,回滚PendingBinlog本身就是一个未解决的难题。

MySQL的Master切换问题

Master如何切换同时也是MySQL容灾中的一个难题。

一个简单的Master切换步骤:

1. Pause旧Master

2. Start新Master

3. 更换MySQLClient的Master指向IP

存在以下几个问题:

1. 当Master被隔离时,如何将其变更为Slave

解决方法:可修改MySQL的代码,使用zookeeper等外部辅助服务来自动维护Master的状态,可解决Master被隔离后不能操作的问题。

2. 如何定位拥有最新Binlog数据的MySQL

解决方法:可以通过人工,或者使用外部工具来检测集群每台MySQL的数据。但当出现故障机器无法访问时,无法定位。

3. 如何进行数据回滚

解决方法:可以通过运维进行人工操作。

4. 如何同时更换MySQLClient的Master指向IP

同时更换所有MySQLClient的Master指向IP是一件不可能的事情,因为不可能同一时刻操作所有机器。

不能同时更换所有MySQLClient的Master指向IP,导致部分Client会向旧Master发送请求,即出现多个Master同时服务。在使用半同步复制的情况下,多台MySQL不能同时知道Master的去向,使得数据可能产生不一致的情况。

图9

图10

假设机器A是旧Master,机器B是新Master,机器C还没收到Master更换的通知仍然向机器A复制Binlog。User1在Master切换前已经连上机器A并持续写入数据。User2在Master切换后开始向机器B写入数据。由于机器A能把数据复制给机器C,机器B能把数据复制给机器A,因此机器A和机器B都能成功写入。如图9。

由于机器A和机器B同时写入数据,数据一致性无法保证。如图10。

总结

从上面分析来看,MySQL的半同步复制和Master切换都存在一些不足。数据复制存在回滚难题,Master切换存在多Master难题。只有解决了这两大难题,才能保证MySQL集群的数据一致性。

MySQL半同步复制的数据一致性探讨微信后台团队实践【转】的更多相关文章

  1. Mysql半同步复制模式说明及配置示例 - 运维小结

    MySQL主从复制包括异步模式.半同步模式.GTID模式以及多源复制模式,默认是异步模式 (如之前详细介绍的mysql主从复制).所谓异步模式指的是MySQL 主服务器上I/O thread 线程将二 ...

  2. mysql半同步复制跟无损半同步区别

    mysql半同步复制跟无损半同步复制的区别: 无损复制其实就是对semi sync增加了rpl_semi_sync_master_wait_point参数,来控制半同步模式下主库在返回给会话事务成功之 ...

  3. MySQL半同步复制

    从MySQL5.5开始,MySQL以插件的形式支持半同步复制.如何理解半同步呢?首先我们来看看异步,全同步的概念 异步复制(Asynchronous replication) MySQL默认的复制即是 ...

  4. 安装MySQL半同步复制

    一.简介 从MySQL5.5开始,MySQL以插件的形式支持半同步复制.如何理解半同步呢?首先我们来看看异步,全同步的概念 异步复制(Asynchronous replication) MySQL默认 ...

  5. MySQL半同步复制(5.5之后引入)

    半同步复制架构在主库提交一个事务后,commit完成即反馈客户端,无需等待推送binlog完成,如图: 半同步复制在主库完成一个事务后,需等待事务信息写入binlog日志并且至少有一个从库写入rela ...

  6. mysql半同步复制实现

    mysql半同步复制和异步复制的区别如上述架构图所看到的:在mysql异步复制的情况下.Mysql Master Server将自己的Binary Log通过复制线程传输出去以后,Mysql Mast ...

  7. MySQL半同步复制搭建

    默认情况下,MySQL 5.5/5.6/5.7和MariaDB 10.0/10.1的复制是异步的,异步复制可以提供最佳性能,主库把binlog日志发送给从库,这一动作就结束了,并不会验证从库是否接收完 ...

  8. (5.5)mysql高可用系列——MySQL半同步复制(实践)

    关键词,mysql半同步复制 [0]实验环境 操作系统:CentOS linux 7.5 数据库版本:5.7.24 数据库架构:主从复制,主库用于生产,从库用于数据容灾和主库备机,采用默认传统的异步复 ...

  9. MySQL半同步复制源码解析

    今天 DBA 同事问了一个问题,MySQL在半同步复制的场景下,当关闭从节点时使得从节点的数量 < rpl_semi_sync_master_wait_for_slave_count时,show ...

随机推荐

  1. neutron openvswitch agent实现安全组的方法

    关于openstack安全组,采用一问一答的形式记录如下 1. 是加载在计算节点的还是网络节点的? 是加载在计算节点的 2. 是使用iptable规则实现的吗? M版的neutron实现了openvs ...

  2. RabbitMQ高可用配置(Haproxy + Keepalived)

    网络结构如下图: 共有104.105.106三台RabbitMQ Server,互为集群 其中104和105安装了Haproxy,每个Haproxy承担三台RabbitMQ server的负载均衡 两 ...

  3. 本地Fiddler传递XML格式数据,调试微信功能。

    一,调试关注和取消关注 User-Agent: Fiddler Host: localhost:4561Content-Type: application/json; charset=utf-8 Co ...

  4. HTML5根据浏览器获取经度和纬度(百度API)

    网页获取用户位置信息的办法1 调用百度地图的地图标注功能,通过百度地图API获取对应的经度和纬度进而获取地区信息 优点是比较准确,缺点是需要用户自己选择位置2 通过H5 geolocation属性获取 ...

  5. 使用EasyBCD 从硬盘安装 deepin2014.1

    EasyBCD config  code: title Install Deepin2014 root (hd0,1) kernel (hd0,1)/vmlinuz boot=casper iso-s ...

  6. 浙大pat 1062题解

    1061. Dating (20) 时间限制 50 ms 内存限制 32000 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Sherlock Holmes ...

  7. NSConditionLock

    一.NSConditionLock定义了一个可以指定条件的互斥锁,用于线程之间的互斥与同步. 这里的条件并不是bool表达式中的条件,而是一个特定的int值. 二.NSConditionLock的AP ...

  8. pthread_create线程创建的过程剖析(转)

    概述 在Linux环境下,pthread库提供的pthread_create()API函数,用于创建一个线程.线程创建失败时,它可能会返回ENOMEM或EAGAIN.这篇文章主要讨论线程创建过程中碰到 ...

  9. 关于mysql中数据类型

    bigint 从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 的整型数据(所有数字).存储大小为 8 个字节. bigint ...

  10. poj 3524 Charm Bracelet(01背包)

    Description Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, she'd ...