详解MySQL的半同步

前言

年后在进行腾讯二面的时候,写完算法的后问的第一个问题就是,MySQL的半同步是什么?我当时直接懵了,我以为是问的MySQL的两阶段提交的问题呢?结果确认了一下后不是两阶段提交,然后面试官看我连问的是啥都不知道,就直接跳过这个问题,直接聊下一个问题了。所以这次总结一下这部分的知识内容,文字内容比较多,可能会有些枯燥,但对于这方面感兴趣的人来说还是比较有意思的。

MySQL的主从复制

我们的一般在大规模的项目上,都是使用MySQL的复制功能来创建MySQL的主从集群的。主要是可以通过为服务器配置一个或多个备库的方式来进行数据同步。复制的功能不仅有利于构建高性能应用,同时也是高可用、可扩展性、灾难恢复、备份以及数据仓库等工作的基础。

说的通俗一点,通过MySQL的主从复制来实现读写分离,相比单点数据库又读又写来说,提升了业务系统性能,优化了用户体验。另外通过主从复制实现了数据库的高可用,当主节点MySQL挂了的时候,可以用从库来顶上。

MySQL支持的复制方式

MySQL支持三种复制方式:

  • 基于语句的复制(也称为逻辑复制)主要是指,在主数据库上执行的SQL语句,在从数据库上会重复执行一遍。MySQL默认采用的就是这种复制,效率比较高。但是也是有一定的问题的,如果SQL中使用uuid()、rand()等函数,那么复制到从库的数据就会有偏差。
  • 基于行的复制,指将更新处理后的数据复制到从数据库,而不是执行一边语句。从MySQL5.1的版本才被支持。
  • 混合复制,默认采用语句复制,当发现语句不能进行精准复制数据时(例如语句中含有uuid()、rand()等函数),采用基于行的复制。

主从复制原理

MySQL的复制原理概述上来讲大体可以分为这三步

  1. 在主库上把数据更改,记录到二进制日志(Binary Log)中。
  2. 从库将主库上的日志复制到自己的中继日志(Relay Log)中。
  3. 备库读取中继日志中的事件,将其重放到备库数据之上。

主要过程如下图:

下面来详细说一下复制的这三步:

第一步:是在主库上记录二进制日志,首先主库要开启binlog日志记录功能,并授权Slave从库可以访问的权限。这里需要注意的一点就是binlog的日志里的顺序是按照事务提交的顺序来记录的而非每条语句的执行顺序。

第二步:从库将binLog复制到其本地的RelayLog中。首先从库会启动一个工作线程,称为I/O线程,I/O线程跟主库建立一个普通的客户端连接,然后主库上启动一个特殊的二进制转储(binlog dump)线程,此转储线程会读取binlog中的事件。当追赶上主库后,会进行休眠,直到主库通知有新的更新语句时才继续被唤醒。 这样通过从库上的I/O线程和主库上的binlog dump线程,就将binlog数据传输到从库上的relaylog中了。

第三步:从库中启动一个SQL线程,从relaylog中读取事件并在备库中执行,从而实现备库数据的更新。

==这种复制架构实现了获取事件和重放事件的解耦,运行I/O线程能够独立于SQL线程之外工作。但是这种架构也限制复制的过程,最重要的一点是在主库上并发运行的查询在备库中只能串行化执行,因为只有一个SQL线程来重放中继日志中的事件。==

说到这个主从复制的串行化执行的问题,我就想到了一个之前在工作中遇到的一个问题,就是有这么一个业务场景,我们有一个操作是初始化一批数据,数据是从一个外部系统的接口中获取的,然后我是通过线程池里的多个线程并行从外部系统的接口中获取数据,每个线程获取到数据后,直接插入到数据库中。然后在数据全部入库完成后,然后去执行批量查询,将刚插入到数据库中的数据查询出来,放到ElasticSearch中。结果每次放入到ES中的数据总是不完整,后来研究了半天都不行,最终是让查询也走的主库才解决的问题。当时不知道是MySQL主从复制的串行化从而导致的这个问题。

MySQL主从复制模式

MySQL的主从复制其实是支持,异步复制、半同步复制、GTID复制等多种复制模式的。

异步模式

MySQL的默认复制模式就是异步模式,主要是指MySQL的主服务器上的I/O线程,将数据写到binlong中就直接返回给客户端数据更新成功,不考虑数据是否传输到从服务器,以及是否写入到relaylog中。在这种模式下,复制数据其实是有风险的,一旦数据只写到了主库的binlog中还没来得急同步到从库时,就会造成数据的丢失。

但是这种模式确也是效率最高的,因为变更数据的功能都只是在主库中完成就可以了,从库复制数据不会影响到主库的写数据操作。

上面我也说了,这种异步复制模式虽然效率高,但是数据丢失的风险很大,所以就有了后面要介绍的半同步复制模式。

半同步模式

MySQL从5.5版本开始通过以插件的形式开始支持半同步的主从复制模式。什么是半同步主从复制模式呢? 这里通过对比的方式来说明一下:

  • 异步复制模式:上面我们已经介绍了,异步复制模式,主库在执行完客户端提交的事务后,只要将执行逻辑写入到binlog后,就立即返回给客户端,并不关心从库是否执行成功,这样就会有一个隐患,就是在主库执行的binlog还没同步到从库时,主库挂了,这个时候从库就就会被强行提升为主库,这个时候就有可能造成数据丢失。
  • 同步复制模式:当主库执行完客户端提交的事务后,需要等到所有从库也都执行完这一事务后,才返回给客户端执行成功。因为要等到所有从库都执行完,执行过程中会被阻塞,等待返回结果,所以性能上会有很严重的影响。
  • 半同步复制模式:半同步复制模式,可以说是介于异步和同步之间的一种复制模式,主库在执行完客户端提交的事务后,要等待至少一个从库接收到binlog并将数据写入到relay log中才返回给客户端成功结果。半同步复制模式,比异步模式提高了数据的可用性,但是也产生了一定的性能延迟,最少要一个TCP/IP连接的往返时间。

半同步复制模式,可以很明确的知道,在一个事务提交成功之后,此事务至少会存在于两个地方一个是主库一个是从库中的某一个。主要原理是,在master的dump线程去通知从库时,增加了一个ACK机制,也就是会确认从库是否收到事务的标志码,master的dump线程不但要发送binlog到从库,还有负责接收slave的ACK。当出现异常时,Slave没有ACK事务,那么将自动降级为异步复制,直到异常修复后再自动变为半同步复制

MySQL半同步复制的流程如下:

半同步复制的隐患
半同步复制模式也存在一定的数据风险,当事务在主库提交完后等待从库ACK的过程中,如果Master宕机了,这个时候就会有两种情况的问题。

  • 事务还没发送到Slave上:若事务还没发送Slave上,客户端在收到失败结果后,会重新提交事务,因为重新提交的事务是在新的Master上执行的,所以会执行成功,后面若是之前的Master恢复后,会以Slave的身份加入到集群中,这个时候,之前的事务就会被执行两次,第一次是之前此台机器作为Master的时候执行的,第二次是做为Slave后从主库中同步过来的。
  • 事务已经同步到Slave上:因为事务已经同步到Slave了,所以当客户端收到失败结果后,再次提交事务,你那么此事务就会再当前Slave机器上执行两次。

为了解决上面的隐患,MySQL从5.7版本开始,增加了一种新的半同步方式。新的半同步方式的执行过程是将“Storage Commit”这一步移动到了“Write Slave dump”后面。这样保证了只有Slave的事务ACK后,才提交主库事务。MySQL 5.7.2版本新增了一个参数来进行配置:rpl_semi_sync_master_wait_point,此参数有两个值可配置:

  • AFTER_SYNC:参数值为AFTER_SYNC时,代表采用的是新的半同步复制方式。
  • AFTER_COMMIT:代表采用的是之前的旧方式的半同步复制模式。

MySQL从5.7.2版本开始,默认的半同步复制方式就是AFTER_SYNC方式了,但是方案不是万能的,因为AFTER_SYNC方式是在事务同步到Slave后才提交主库的事务的,若是当主库等待Slave同步成功的过程中Master挂了,这个Master事务提交就失败了,客户端也收到了事务执行失败的结果了,但是Slave上已经将binLog的内容写到Relay Log里了,这个时候,Slave数据就会多了,但是多了数据一般问题不算严重,多了总比少了好。MySQL,在没办法解决分布式数据一致性问题的情况下,它能保证的是不丢数据,多了数据总比丢数据要好。

这里说几个的半同步复制模式的参数:

mysql> show variables like '%Rpl%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | ON         |
| rpl_semi_sync_master_timeout              | 10000      |
| rpl_semi_sync_master_trace_level          | 32         |
| rpl_semi_sync_master_wait_for_slave_count | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
| rpl_stop_slave_timeout                    | 31536000   |
+-------------------------------------------+------------+
-- 半同步复制模式开关
rpl_semi_sync_master_enabled
-- 半同步复制,超时时间,单位毫秒,当超过此时间后,自动切换为异步复制模式
rpl_semi_sync_master_timeout
-- MySQL 5.7.3引入的,该变量设置主需要等待多少个slave应答,才能返回给客户端,默认为1。
rpl_semi_sync_master_wait_for_slave_count
-- 此值代表当前集群中的slave数量是否还能够满足当前配置的半同步复制模式,默认为ON,当不满足半同步复制模式后,全部Slave切换到异步复制,此值也会变为OFF
rpl_semi_sync_master_wait_no_slave
-- 代表半同步复制提交事务的方式,5.7.2之后,默认为AFTER_SYNC
rpl_semi_sync_master_wait_point

GTID模式

MySQL从5.6版本开始推出了GTID复制模式,GTID即全局事务ID (global transaction identifier)的简称,GTID是由UUID+TransactionId组成的,UUID是单个MySQL实例的唯一标识,在第一次启动MySQL实例时会自动生成一个server_uuid, 并且默认写入到数据目录下的auto.cnf(mysql/data/auto.cnf)文件里。TransactionId是该MySQL上执行事务的数量,随着事务数量增加而递增。这样保证了GTID在一组复制中,全局唯一。

这样通过GTID可以清晰的看到,当前事务是从哪个实例上提交的,提交的第多少个事务。

来看一个GTID的具体形式:

mysql> show master status;
+-----------+----------+--------------+------------------+-------------------------------------------+
| File      | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+-----------+----------+--------------+------------------+-------------------------------------------+
| on.000003 |      187 |              |                  | 76147e28-8086-4f8c-9f98-1cf33d92978d:1-322|
+-----------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)

GTID的工作原理

由于GTID在一组主从复制集群中的唯一性,从而保证了每个GTID的事务只在一个MySQL上执行一次。 那么是怎么实现这种机制的呢?GTID的原理又是什么样的呢?

当从服务器连接主服务器时,把自己执行过的GTID(Executed_Gtid_Set: 即已经执行的事务编码)以及获取到GTID(Retrieved_Gtid_Set: 即从库已经接收到主库的事务编号)都传给主服务器。主服务器会从服务器缺少的GTID以及对应的transactionID都发送给从服务器,让从服务器补全数据。当主服务器宕机时,会找出同步数据最成功的那台conf服务器,直接将它提升为主服务器。若是强制要求某一台不是同步最成功的一台从服务器为主,会先通过change命令到最成功的那台服务器,将GTID进行补全,然后再把强制要求的那台机器提升为主。

主要数据同步机制可以分为这几步:

  • ==master更新数据时,在事务前生产GTID,一同记录到binlog中。==
  • ==slave端的i/o线程,将变更的binlog写入到relay log中。==
  • ==sql线程从relay log中获取GTID,然后对比Slave端的binlog是否有记录。==
  • ==如果有记录,说明该GTID的事务已经执行,slave会忽略该GTID。==
  • ==如果没有记录,Slave会从relay log中执行该GTID事务,并记录到binlog。==
  • ==在解析过程中,判断是否有主键,如果没有主键就使用二级索引,再没有二级索引就扫描全表。==

初始结构如下图

通过上图我们可以看出来,当Master挂掉后,Slave-1执行完了Master的事务,Slave-2延时一些,所以没有执行完Master的事务,这个时候提升Slave-1为主,Slave-2连接了新主(Slave-1)后,将最新的GTID传给新主,然后Slave-1就从这个GTID的下一个GTID开始发送事务给Slave-2。这种自我寻找复制位置的模式减少事务丢失的可能性以及故障恢复的时间。

GTID的优劣势

通过上面的分析我们可以得出GTID的优势是:

  • ==每一个事务对应一个执行ID,一个GTID在一个服务器上只会执行一次;==
  • ==GTID是用来代替传统复制的方法,GTID复制与普通复制模式的最大不同就是不需要指定二进制文件名和位置;==
  • ==减少手工干预和降低服务故障时间,当主机挂了之后通过软件从众多的备机中提升一台备机为主机;==

GTID的缺点也很明显:

  • ==首先不支持非事务的存储引擎;==
  • ==不支持create table ... select 语句复制(主库直接报错);(原理: 会生成两个sql, 一个是DDL创建表SQL, 一个是insert into 插入数据的sql; 由于DDL会导致自动提交, 所以这个sql至少需要两个GTID, 但是GTID模式下, 只能给这个sql生成一个GTID)==
  • ==不允许一个SQL同时更新一个事务引擎表和非事务引擎表;==
  • ==在一个MySQL复制群组中,要求全部开启GTID或关闭GTID。==
  • ==开启GTID需要重启 (mysql5.7除外);==
  • ==开启GTID后,就不再使用原来的传统复制方式(不像半同步复制,半同步复制失败后,可以降级到异步复制);==
  • ==对于create temporary table 和 drop temporary table语句不支持;==
  • ==不支持sql_slave_skip_counter;==

其实GTID的这部分内容挺多的,如果有想深入研究的可以去看看这篇文章。 最后说几个开启GTID的必备条件:

  • MySQL 5.6 版本,在my.cnf文件中添加:
gtid_mode=on (必选)                    #开启gtid功能
log_bin=log-bin=mysql-bin (必选)       #开启binlog二进制日志功能
log-slave-updates=1 (必选)             #也可以将1写为on
enforce-gtid-consistency=1 (必选)      #也可以将1写为on
  • MySQL 5.7或更高版本,在my.cnf文件中添加:
gtid_mode=on    (必选)
enforce-gtid-consistency=1  (必选)
log_bin=mysql-bin           (可选)    #高可用切换,最好开启该功能
log-slave-updates=1     (可选)       #高可用切换,最好打开该功能

以上就是详解MySQL的半同步的详细内容,更多关于MySQL的半同步的资料请关注我们其它相关文章!

(0)

相关推荐

  • MYSQL 完全备份、主从复制、级联复制、半同步小结

    mysql 完全备份 1,启用二进制日志,并于数据库分离,单独存放 vim /etc/my.cnf 添加 log_bin=/data/bin/mysql-bin 创建/data/bin文件夹并授权 chown mysql.mysql /data/bin 2,完成备份数据库 mysqldump -A --single-transaction --master-data=2 | xz > /data/all.sql.xz 3,对数据库进行增删改 INSERT hellodb.students(stu

  • Mysql半同步复制原理及问题排查

    mysql半同步复制和异步复制的差别如上述架构图所示:在mysql异步复制的情况下,Mysql Master Server将自己的Binary Log通过复制线程传输出去以后,Mysql Master Sever就自动返回数据给客户端,而不管slave上是否接受到了这个二进制日志.在半同步复制的架构下,当master在将自己binlog发给slave上的时候,要确保slave已经接受到了这个二进制日志以后,才会返回数据给客户端.对比两种架构:异步复制对于用户来说,可以确保得到快速的响应结构,但是

  • MySQL半同步复制原理配置与介绍详解

    环境介绍: Ubuntu Server 16.04.2+MySQL 5.7.17 Community Server (GPL) MySQL安装 通过APT的方式安装,官方指导文档地址: https://dev.mysql.com/downloads/repo/apt/ 1.下载mysql-apt-config_0.8.3-1_all.deb 2.安装deb A Quick Guide to Using the MySQL APT Repository: https://dev.mysql.com

  • 深入解析半同步与异步的MySQL主从复制配置

    简单来讲MySQL的主从复制就是一个C/S架构的应用.master可以认为是我们通常意义上所认为的server,slave可以当作是一台client.slave上的I/O线程去请求master上数据,而master验证通过slave的信息后就允许slave接入,然后进行数据变化信息的发送. 一.MySQL主从复制原理 这里我以MySQL5.5为例来说一下MySQL的主从复制的原理: 首先由备节点的I/O线程负责向主节点请求数据,主节点验证通过以后会由dump线程把数据发送给备用节点.备用节点的I

  • 简单谈谈MySQL的半同步复制

    简介 MySQL通过复制(Replication)实现存储系统的高可用.目前,MySQL支持的复制方式有: 异步复制(Asynchronous Replication):原理最简单,性能最好.但是主备之间数据不一致的概率很大. 半同步复制(Semi-synchronous Replication):相比异步复制,半同步复制牺牲了一定的性能,提升了主备之间数据的一致性(有一些情况还是会出现主备数据不一致). 组复制(Group Replication):基于Paxos算法实现分布式数据复制的强一致

  • 详解MySQL的半同步

    前言 年后在进行腾讯二面的时候,写完算法的后问的第一个问题就是,MySQL的半同步是什么?我当时直接懵了,我以为是问的MySQL的两阶段提交的问题呢?结果确认了一下后不是两阶段提交,然后面试官看我连问的是啥都不知道,就直接跳过这个问题,直接聊下一个问题了.所以这次总结一下这部分的知识内容,文字内容比较多,可能会有些枯燥,但对于这方面感兴趣的人来说还是比较有意思的. MySQL的主从复制 我们的一般在大规模的项目上,都是使用MySQL的复制功能来创建MySQL的主从集群的.主要是可以通过为服务器配

  • 一文详解MySQL主从同步原理

    目录 1. MySQL主从同步实现方式 2. MySQL主从同步的作用 一主多从架构 双主多从架构 3. 主动同步的原理 4. 主从同步延迟问题 主从同步延迟的原因有哪些? 主从同步延迟的解决方案? 5. 如何提升主从同步性能 从库开启多线程复制 修改同步模式,改为异步 修改从库Bin Log配置 知识点总结 1. MySQL主从同步实现方式 MySQL主从同步是基于Bin Log实现的,而Bin Log记录的是原始SQL语句. Bin Log共有三种日志格式,可以binlog_format配置

  • 详解Mysql如何实现数据同步到Elasticsearch

    目录 一.同步原理 二.logstash-input-jdbc 三.go-mysql-elasticsearch 四.elasticsearch-jdbc 五.logstash-input-jdbc实现同步 六.go-mysql-elasticsearch实现同步 七.elasticsearch-jdbc实现同步 一.同步原理 基于Mysql的binlog日志订阅:binlog日志是Mysql用来记录数据实时的变化 Mysql数据同步到ES中分为两种,分别是全量同步和增量同步 全量同步表示第一次

  • 详解MySQL实时同步到Oracle解决方案

    1 需求概述 将MySQL5.6生产库多张表的数据实时同步到Oracle11g数据仓库,MySQL历史数据700G,平均每天产生50G左右日志文件,MySQL日志空间50G,超过后滚动删除日志文件.整个同步过程不可影响MySQL业务操作. 2 技术原理 采用灵蜂数据集成软件BeeDI将MySQL数据实时同步到Oracle,通过ETL全量同步历史数据,通过日志解析方式实时同步增量数据. 受限于日志空间,如果将所有历史数据一次性同步,需要的时间会超过一天,全量同步过程产生的日志会被删除,造成实时日志

  • 详解MySQL主从复制及读写分离

    前言 在企业实际应用中,成熟的业务通常数据量都比较大,而单台MySQL服务器在安全性.高可用性和高并发方面都无法满足实际的需求,我们可以在多台MySQL服务器(Master-Slave)部署 主从复制来实现同步数据,再通过 读写分离来提升数据库的并发负载能力.有点类似于rsync,但是不同的是rsync是对磁盘文件做备份,而mysql主从复制是对数据库中的数据.语句做备份. 一.相关概述 主从复制:主数据库(Master)发送更新事件到从数据库(Slave),从数据库读取更新记录,并执行更新记录

  • 一文详解MySQL Binlog日志与主从复制

    目录 1. Binlog日志的介绍 2. 主从复制 2.1 主从复制的流程 2.2 GTID 2.3 复制模型 2.4 MGR模式 2.5 并行回放 1. Binlog日志的介绍 Binlog是Binary log的缩写,即二进制日志.Binlog主要有三个作用:持久化时将随机IO转化为顺序IO,主从复制以及数据恢复.本文重点主从复制相关的问题. Binlog日志由一个索引文件与很多日志文件组成,每个日志文件由魔数以及事件组成,每个日志文件都会以一个Rotate类型的事件结束. 对于每个事件,都

  • 详解 Mysql 事务和Mysql 日志

    事务特性 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节. 2.一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 .比如A向B转账,不可能A扣了钱,B却没收到. 3.隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰.比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账. 4.持久性(Durability):事务完成后,事务对数据库的所有更新

  • 详解mysql中explain的type

    导语: 很多情况下,有很多人用各种select语句查询到了他们想要的数据后,往往便以为工作圆满结束了. 这些事情往往发生在一些学生亦或刚入职场但之前又没有很好数据库基础的小白身上,但所谓闻道有先后,只要我们小白好好学习,天天向上,还是很靠谱的. 当一个sql查询语句被写出来之后,其实你的工作只完成了一小半,接下来更重要的工作是评估你自己写的sql的质量与效率.mysql为我们提供了很有用的辅助武器explain,它向我们展示了mysql接收到一条sql语句的执行计划.根据explain返回的结果

  • 详解MySQL数据库千万级数据查询和存储

    百万级数据处理方案 数据存储结构设计 表字段设计 表字段 not null,因为 null 值很难查询优化且占用额外的索引空间,推荐默认数字 0. 数据状态类型的字段,比如 status, type 等等,尽量不要定义负数,如 -1.因为这样可以加上 UNSIGNED,数值容量就会扩大一倍. 可以的话用 TINYINT.SMALLINT 等代替 INT,尽量不使用 BIGINT,因为占的空间更小. 字符串类型的字段会比数字类型占的空间更大,所以尽量用整型代替字符串,很多场景是可以通过编码逻辑来实

  • 详解MySQL连接挂死的原因

    一.背景 近期由测试反馈的问题有点多,其中关于系统可靠性测试提出的问题令人感到头疼,一来这类问题有时候属于"偶发"现象,难以在环境上快速复现:二来则是可靠性问题的定位链条有时候变得很长,极端情况下可能要从 A 服务追踪到 Z 服务,或者是从应用代码追溯到硬件层面. 本次分享的是一次关于 MySQL 高可用问题的定位过程,其中曲折颇多但问题本身却比较有些代表性,遂将其记录以供参考. 架构 首先,本系统以 MySQL 作为主要的数据存储部件.整一个是典型的微服务架构(SpringBoot

随机推荐